You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2017/09/14 15:13:04 UTC
[1/4] nifi git commit: NIFI-4280: - Adding support for the user to
configure variables in the UI. - Updating the endpoints for changing
variables as necessary. This closes #2135.
Repository: nifi
Updated Branches:
refs/heads/master 91383264d -> eac47e90c
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-variable-registry.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-variable-registry.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-variable-registry.js
new file mode 100644
index 0000000..47b77ac
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-variable-registry.js
@@ -0,0 +1,1633 @@
+/*
+ * 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 */
+
+/**
+ * Opens the variable registry for a given Process Group.
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['jquery',
+ 'd3',
+ 'Slick',
+ 'nf.Canvas',
+ 'nf.CanvasUtils',
+ 'nf.ErrorHandler',
+ 'nf.Dialog',
+ 'nf.Client',
+ 'nf.Common',
+ 'nf.ng.Bridge',
+ 'nf.Processor',
+ 'nf.ProcessGroup',
+ 'nf.ProcessGroupConfiguration'],
+ function ($, d3, Slick, nfCanvas, nfCanvasUtils, nfErrorHandler, nfDialog, nfClient, nfCommon, nfNgBridge, nfProcessor, nfProcessGroup, nfProcessGroupConfiguration) {
+ return (nf.ComponentState = factory($, d3, Slick, nfCanvas, nfCanvasUtils, nfErrorHandler, nfDialog, nfClient, nfCommon, nfNgBridge, nfProcessor, nfProcessGroup, nfProcessGroupConfiguration));
+ });
+ } else if (typeof exports === 'object' && typeof module === 'object') {
+ module.exports = (nf.ComponentState =
+ factory(require('jquery'),
+ require('d3'),
+ require('Slick'),
+ require('nf.Canvas'),
+ require('nf.CanvasUtils'),
+ require('nf.ErrorHandler'),
+ require('nf.Dialog'),
+ require('nf.Client'),
+ require('nf.Common'),
+ require('nf.ng.Bridge'),
+ require('nf.Processor'),
+ require('nf.ProcessGroup'),
+ require('nf.ProcessGroupConfiguration')));
+ } else {
+ nf.VariableRegistry = factory(root.$,
+ root.d3,
+ root.Slick,
+ root.nf.Canvas,
+ root.nf.CanvasUtils,
+ root.nf.ErrorHandler,
+ root.nf.Dialog,
+ root.nf.Client,
+ root.nf.Common,
+ root.nf.ng.Bridge,
+ root.nf.Processor,
+ root.nf.ProcessGroup,
+ root.nf.ProcessGroupConfiguration);
+ }
+}(this, function ($, d3, Slick, nfCanvas, nfCanvasUtils, nfErrorHandler, nfDialog, nfClient, nfCommon, nfNgBridge, nfProcessor, nfProcessGroup, nfProcessGroupConfiguration) {
+ 'use strict';
+
+ // text editor
+ var textEditor = function (args) {
+ var scope = this;
+ var initialValue = '';
+ var previousValue;
+ var wrapper;
+ var isEmpty;
+ var input;
+
+ this.init = function () {
+ var container = $('body');
+
+ // record the previous value
+ previousValue = args.item[args.column.field];
+
+ // create the wrapper
+ wrapper = $('<div></div>').addClass('slickgrid-editor').css({
+ 'z-index': 100000,
+ 'position': 'absolute',
+ 'border-radius': '2px',
+ 'box-shadow': 'rgba(0, 0, 0, 0.247059) 0px 2px 5px',
+ 'background-color': 'rgb(255, 255, 255)',
+ 'overflow': 'hidden',
+ 'padding': '10px 20px',
+ 'cursor': 'move',
+ 'transform': 'translate3d(0px, 0px, 0px)'
+ }).appendTo(container);
+
+ // create the input field
+ input = $('<textarea hidefocus rows="5"/>').css({
+ 'height': '80px',
+ 'width': args.position.width + 'px',
+ 'min-width': '212px',
+ 'margin-bottom': '5px',
+ 'margin-top': '10px',
+ 'white-space': 'pre'
+ }).tab().on('keydown', scope.handleKeyDown).appendTo(wrapper);
+
+ wrapper.draggable({
+ cancel: '.button, textarea, .nf-checkbox',
+ containment: 'parent'
+ });
+
+ // create the button panel
+ var stringCheckPanel = $('<div class="string-check-container">');
+ stringCheckPanel.appendTo(wrapper);
+
+ // build the custom checkbox
+ isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
+ $('<span class="string-check-label nf-checkbox-label"> Set empty string</span>').appendTo(stringCheckPanel);
+
+ var ok = $('<div class="button">Ok</div>').css({
+ 'color': '#fff',
+ 'background': '#728E9B'
+ }).hover(
+ function () {
+ $(this).css('background', '#004849');
+ }, function () {
+ $(this).css('background', '#728E9B');
+ }).on('click', scope.save);
+ var cancel = $('<div class="secondary-button">Cancel</div>').css({
+ 'color': '#004849',
+ 'background': '#E3E8EB'
+ }).hover(
+ function () {
+ $(this).css('background', '#C7D2D7');
+ }, function () {
+ $(this).css('background', '#E3E8EB');
+ }).on('click', scope.cancel);
+ $('<div></div>').css({
+ 'position': 'relative',
+ 'top': '10px',
+ 'left': '20px',
+ 'width': '212px',
+ 'clear': 'both',
+ 'float': 'right'
+ }).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
+
+ // position and focus
+ scope.position(args.position);
+ input.focus().select();
+ };
+
+ this.handleKeyDown = function (e) {
+ if (e.which === $.ui.keyCode.ENTER && !e.shiftKey) {
+ scope.save();
+ } else if (e.which === $.ui.keyCode.ESCAPE) {
+ scope.cancel();
+
+ // prevent further propagation or escape press and prevent default behavior
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ }
+ };
+
+ this.save = function () {
+ args.commitChanges();
+ };
+
+ this.cancel = function () {
+ input.val(initialValue);
+ args.cancelChanges();
+ };
+
+ this.hide = function () {
+ wrapper.hide();
+ };
+
+ this.show = function () {
+ wrapper.show();
+ };
+
+ this.position = function (position) {
+ wrapper.css({
+ 'top': position.top - 27,
+ 'left': position.left - 20
+ });
+ };
+
+ this.destroy = function () {
+ wrapper.remove();
+ };
+
+ this.focus = function () {
+ input.focus();
+ };
+
+ this.loadValue = function (item) {
+ var isEmptyChecked = false;
+
+ // determine the value to use when populating the text field
+ if (nfCommon.isDefinedAndNotNull(item[args.column.field])) {
+ initialValue = item[args.column.field];
+ isEmptyChecked = initialValue === '';
+ }
+
+ // determine if its an empty string
+ var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
+ isEmpty.addClass(checkboxStyle);
+
+ input.val(initialValue);
+ input.select();
+ };
+
+ this.serializeValue = function () {
+ // if the field has been cleared, set the value accordingly
+ if (input.val() === '') {
+ // if the user has checked the empty string checkbox, use emtpy string
+ if (isEmpty.hasClass('checkbox-checked')) {
+ return '';
+ } else {
+ return null;
+ }
+ } else {
+ // if there is text specified, use that value
+ return input.val();
+ }
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return scope.serializeValue() !== previousValue;
+ };
+
+ this.validate = function () {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ // initialize the custom long text editor
+ this.init();
+ };
+
+ /**
+ * Shows the variable in a read only property detail winder.
+ *
+ * @param {object} variable
+ * @param {slickgrid} variableGrid
+ * @param {integer} row
+ * @param {integer} cell
+ */
+ var showVariableValue = function (variable, variableGrid, row, cell) {
+ var cellNode = $(variableGrid.getCellNode(row, cell));
+ var offset = cellNode.offset();
+
+ var wrapper = $('<div class="property-detail"></div>').css({
+ 'z-index': 1999,
+ 'position': 'absolute',
+ 'padding': '10px 20px',
+ 'overflow': 'hidden',
+ 'border-radius': '2px',
+ 'box-shadow': 'rgba(0, 0, 0, 0.247059) 0px 2px 5px',
+ 'background-color': 'rgb(255, 255, 255)',
+ 'cursor': 'move',
+ 'transform': 'translate3d(0px, 0px, 0px)',
+ 'top': offset.top - 26,
+ 'left': offset.left - 20
+ }).draggable({
+ containment: 'parent'
+ }).appendTo('body');
+
+ // create the input field
+ $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
+ 'height': '80px',
+ 'resize': 'both',
+ 'width': cellNode.width() + 'px',
+ 'margin': '10px 0px',
+ 'white-space': 'pre'
+ }).text(variable.value).on('keydown', function (evt) {
+ if (evt.which === $.ui.keyCode.ESCAPE) {
+ cleanUp();
+
+ evt.stopImmediatePropagation();
+ evt.preventDefault();
+ }
+ }).appendTo(wrapper);
+
+ var cleanUp = function () {
+ wrapper.hide().remove();
+ };
+
+ // add an ok button that will remove the entire pop up
+ var ok = $('<div class="button">Ok</div>').css({
+ 'position': 'relative',
+ 'top': '10px',
+ 'left': '20px'
+ }).hover(
+ function () {
+ $(this).css('background', '#004849');
+ }, function () {
+ $(this).css('background', '#728E9B');
+ }).on('click', function () {
+ cleanUp();
+ });
+
+ $('<div></div>').append(ok).append('<div class="clear"></div>').appendTo(wrapper);
+ };
+
+ var gridOptions = {
+ forceFitColumns: true,
+ enableTextSelectionOnCells: true,
+ enableCellNavigation: true,
+ enableColumnReorder: false,
+ editable: true,
+ enableAddRow: false,
+ autoEdit: false,
+ multiSelect: false,
+ rowHeight: 24
+ };
+
+ /**
+ * Gets the scope label for the specified Process Group.
+ *
+ * @param {string} processGroupId
+ * @returns {string} the label for the specified Process Group
+ */
+ var getScopeLabel = function (processGroupId) {
+ // see if this listing is based off a selected process group
+ var selection = nfCanvasUtils.getSelection();
+ if (selection.empty() === false) {
+ var selectedData = selection.datum();
+ if (selectedData.id === processGroupId) {
+ if (selectedData.permissions.canRead) {
+ return nfCommon.escapeHtml(selectedData.component.name);
+ } else {
+ return nfCommon.escapeHtml(selectedData.id);
+ }
+ }
+ }
+
+ // there's either no selection or the variable is defined in an ancestor component
+ var breadcrumbs = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
+
+ var processGroupLabel = processGroupId;
+ $.each(breadcrumbs, function (_, breadcrumbEntity) {
+ if (breadcrumbEntity.id === processGroupId) {
+ processGroupLabel = breadcrumbEntity.label;
+ return false;
+ }
+ });
+
+ return processGroupLabel;
+ };
+
+ /**
+ * Initializes the variable table
+ */
+ var initVariableTable = function () {
+ var variableTable = $('#variable-registry-table');
+
+ var nameFormatter = function (row, cell, value, columnDef, dataContext) {
+ return nfCommon.escapeHtml(value);
+ };
+
+ var valueFormatter = function (row, cell, value, columnDef, dataContext) {
+ if (dataContext.isOverridden) {
+ return '<div class="overridden" title="This value has been overridden by another variable in a descendant Process Group">' + nfCommon.escapeHtml(value) + '</div>';
+ } else {
+ if (value === '') {
+ return '<span class="table-cell blank">Empty string set</span>';
+ } else if (value === null) {
+ return '<span class="unset">No value set</span>';
+ } else {
+ return nfCommon.escapeHtml(value);
+ }
+ }
+ };
+
+ var scopeFormatter = function (row, cell, value, columnDef, dataContext) {
+ if (nfCommon.isDefinedAndNotNull(value)) {
+ return nfCommon.escapeHtml(getScopeLabel(value));
+ } else {
+ return 'Controller';
+ }
+ };
+
+ var variableActionFormatter = function (row, cell, value, columnDef, dataContext) {
+ var markup = '';
+
+ if (dataContext.isEditable === true) {
+ markup += '<div title="Delete" class="delete-variable pointer fa fa-trash" style="margin-top: 2px;" ></div>';
+ } else {
+ var currentProcessGroupId = $('#variable-registry-process-group-id').text();
+
+ if (dataContext.processGroupId !== currentProcessGroupId) {
+ markup += '<div title="Go To" class="go-to-variable pointer fa fa-long-arrow-right" style="margin-top: 2px;" ></div>';
+ }
+ }
+
+ return markup;
+ };
+
+ // define the column model for the controller services table
+ var variableColumns = [
+ {
+ id: 'scope',
+ name: 'Scope',
+ field: 'processGroupId',
+ formatter: scopeFormatter,
+ sortable: true,
+ resizable: true
+ },
+ {
+ id: 'name',
+ name: 'Name',
+ field: 'name',
+ formatter: nameFormatter,
+ sortable: true,
+ resizable: true
+ },
+ {
+ id: 'value',
+ name: 'Value',
+ field: 'value',
+ formatter: valueFormatter,
+ sortable: true,
+ resizable: true,
+ cssClass: 'pointer'
+ },
+ {
+ id: 'actions',
+ name: ' ',
+ resizable: false,
+ formatter: variableActionFormatter,
+ sortable: false,
+ width: 45,
+ maxWidth: 45
+ }
+ ];
+
+ // initialize the dataview
+ var variableData = new Slick.Data.DataView({
+ inlineFilters: false
+ });
+ variableData.setFilterArgs({
+ searchString: '',
+ property: 'hidden'
+ });
+ variableData.setFilter(function (item, args) {
+ return item.hidden === false;
+ });
+ variableData.getItemMetadata = function (index) {
+ return {
+ columns: {
+ value: {
+ editor: textEditor
+ }
+ }
+ };
+ };
+
+ // initialize the sort
+ sortVariables({
+ columnId: 'name',
+ sortAsc: true
+ }, variableData);
+
+ // initialize the grid
+ var variablesGrid = new Slick.Grid(variableTable, variableData, variableColumns, gridOptions);
+ variablesGrid.setSelectionModel(new Slick.RowSelectionModel());
+ variablesGrid.registerPlugin(new Slick.AutoTooltips());
+ variablesGrid.setSortColumn('name', true);
+ variablesGrid.onSort.subscribe(function (e, args) {
+ sortVariables({
+ columnId: args.sortCol.id,
+ sortAsc: args.sortAsc
+ }, variableData);
+ });
+ variablesGrid.onClick.subscribe(function (e, args) {
+ // get the variable at this row
+ var variable = variableData.getItem(args.row);
+
+ if (variablesGrid.getColumns()[args.cell].id === 'value') {
+ if (variable.isEditable === true) {
+ variablesGrid.gotoCell(args.row, args.cell, true);
+ } else {
+ // ensure the row is selected
+ variablesGrid.setSelectedRows([args.row]);
+
+ // show the variable
+ showVariableValue(variable, variablesGrid, args.row, args.cell);
+ }
+
+ // prevents standard edit logic
+ e.stopImmediatePropagation();
+ } else if (variablesGrid.getColumns()[args.cell].id === 'actions') {
+ var target = $(e.target);
+
+ // determine the desired action
+ if (target.hasClass('delete-variable')) {
+ // mark the property in question for removal and refresh the table
+ variableData.updateItem(variable.id, $.extend(variable, {
+ hidden: true
+ }));
+
+ // look if this variable that was just 'removed'
+ var variables = variableData.getItems();
+ $.each(variables, function (_, item) {
+ if (item.isOverridden === true && !isOverridden(variables, item)) {
+ variableData.updateItem(item.id, $.extend(item, {
+ isOverridden: false
+ }));
+ }
+ });
+
+ // reset the selection if necessary
+ var selectedRows = variablesGrid.getSelectedRows();
+ if (selectedRows.length === 0) {
+ variablesGrid.setSelectedRows([0]);
+ }
+
+ // prevents standard edit logic
+ e.stopImmediatePropagation();
+ } else if (target.hasClass('go-to-variable')) {
+ // check if there are outstanding changes
+ handleOutstandingChanges().done(function () {
+ // go to the process group that this variable belongs to
+ var breadcrumbs = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
+ $.each(breadcrumbs, function (_, breadcrumbEntity) {
+ // find the breadcrumb for the process group of the variable
+ if (breadcrumbEntity.id === variable.processGroupId) {
+ // if that breadcrumb has a parent breadcrumb, navigate to the parent group and select the PG
+ if (nfCommon.isDefinedAndNotNull(breadcrumbEntity.parentBreadcrumb)) {
+ nfCanvasUtils.showComponent(breadcrumbEntity.parentBreadcrumb.id, breadcrumbEntity.id).done(function () {
+ setTimeout(function () {
+ // open the variable dialog for the process group of this variable
+ showVariables(variable.processGroupId, variable.name);
+ }, 500);
+ });
+ } else {
+ nfCanvasUtils.getComponentByType('ProcessGroup').enterGroup(breadcrumbEntity.id).done(function () {
+ setTimeout(function () {
+ // open the variable dialog for the process group of this variable
+ showVariables(variable.processGroupId, variable.name);
+ }, 500);
+ });
+ }
+
+ return false;
+ }
+ });
+ });
+ }
+ }
+ });
+ variablesGrid.onSelectedRowsChanged.subscribe(function (e, args) {
+ if ($.isArray(args.rows) && args.rows.length === 1) {
+ // show the affected components for the selected variable
+ if (variablesGrid.getDataLength() > 0) {
+ var variableIndex = args.rows[0];
+ var variable = variablesGrid.getDataItem(variableIndex);
+
+ // update the details for this variable
+ $('#affected-components-context').removeClass('unset').text(variable.name);
+ populateAffectedComponents(variable.affectedComponents);
+ }
+ }
+ });
+
+ // wire up the dataview to the grid
+ variableData.onRowCountChanged.subscribe(function (e, args) {
+ variablesGrid.updateRowCount();
+ variablesGrid.render();
+ });
+ variableData.onRowsChanged.subscribe(function (e, args) {
+ variablesGrid.invalidateRows(args.rows);
+ variablesGrid.render();
+ });
+ variableData.syncGridSelection(variablesGrid, true);
+
+ // hold onto an instance of the grid
+ variableTable.data('gridInstance', variablesGrid);
+ };
+
+ /**
+ * Handles outstanding changes.
+ *
+ * @returns {deferred}
+ */
+ var handleOutstandingChanges = function () {
+ var variableGrid = $('#variable-registry-table').data('gridInstance');
+ if (nfCommon.isDefinedAndNotNull(variableGrid)) {
+ // get the property grid to commit the current edit
+ var editController = variableGrid.getEditController();
+ editController.commitCurrentEdit();
+ }
+
+ return $.Deferred(function (deferred) {
+ if ($('#variable-update-status').is(':visible')) {
+ close();
+ deferred.resolve();
+ } else {
+ var variables = marshalVariables();
+
+ // if there are no variables there is nothing to save
+ if ($.isEmptyObject(variables)) {
+ close();
+ deferred.resolve();
+ } else {
+ // see if those changes should be saved
+ nfDialog.showYesNoDialog({
+ headerText: 'Variables',
+ dialogContent: 'Save changes before leaving variable configuration?',
+ noHandler: function () {
+ close();
+ deferred.resolve();
+ },
+ yesHandler: function () {
+ updateVariables().done(function () {
+ deferred.resolve();
+ }).fail(function () {
+ deferred.reject();
+ });
+ }
+ });
+ }
+ }
+
+ }).promise();
+ };
+
+ /**
+ * Sorts the specified data using the specified sort details.
+ *
+ * @param {object} sortDetails
+ * @param {object} data
+ */
+ var sortVariables = function (sortDetails, data) {
+ // defines a function for sorting
+ var comparer = function (a, b) {
+ if (sortDetails.columnId === 'scope') {
+ var aScope = nfCommon.isDefinedAndNotNull(a.processGroupId) ? getScopeLabel(a.processGroupId) : '';
+ var bScope = nfCommon.isDefinedAndNotNull(b.processGroupId) ? getScopeLabel(b.processGroupId) : '';
+ return aScope === bScope ? 0 : aScope > bScope ? 1 : -1;
+ } else {
+ var aString = nfCommon.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
+ var bString = nfCommon.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
+ return aString === bString ? 0 : aString > bString ? 1 : -1;
+ }
+ };
+
+ // perform the sort
+ data.sort(comparer, sortDetails.sortAsc);
+ };
+
+ /**
+ * Sorts the specified entities based on the name.
+ *
+ * @param {object} a
+ * @param {pbject} b
+ * @returns {number}
+ */
+ var nameComparator = function (a, b) {
+ return a.component.name.localeCompare(b.component.name);
+ };
+
+ /**
+ * Renders the specified affected component.
+ *
+ * @param {object} affectedProcessorEntity
+ * @param {jQuery} container
+ */
+ var renderAffectedProcessor = function (affectedProcessorEntity, container) {
+ var affectedProcessorContainer = $('<li class="affected-component-container"></li>').appendTo(container);
+ var affectedProcessor = affectedProcessorEntity.component;
+
+ // processor state
+ $('<div class="referencing-component-state"></div>').addClass(function () {
+ if (nfCommon.isDefinedAndNotNull(affectedProcessor.state)) {
+ var icon = $(this);
+
+ var state = affectedProcessor.state.toLowerCase();
+ if (state === 'stopped' && !nfCommon.isEmpty(affectedProcessor.validationErrors)) {
+ state = 'invalid';
+
+ // build the validation error listing
+ var list = nfCommon.formatUnorderedList(affectedProcessor.validationErrors);
+
+ // add tooltip for the warnings
+ icon.qtip($.extend({},
+ nfCanvasUtils.config.systemTooltipConfig,
+ {
+ content: list
+ }));
+ }
+
+ return state;
+ } else {
+ return '';
+ }
+ }).appendTo(affectedProcessorContainer);
+
+
+ // processor name
+ $('<span class="referencing-component-name link"></span>').text(affectedProcessor.name).on('click', function () {
+ // check if there are outstanding changes
+ handleOutstandingChanges().done(function () {
+ // show the component in question
+ nfCanvasUtils.showComponent(affectedProcessor.processGroupId, affectedProcessor.id);
+ });
+ }).appendTo(affectedProcessorContainer);
+
+ // bulletin
+ $('<div class="referencing-component-bulletins"></div>').addClass(affectedProcessor.id + '-affected-bulletins').appendTo(affectedProcessorContainer);
+
+ // processor active threads
+ $('<span class="referencing-component-active-thread-count"></span>').text(function () {
+ if (nfCommon.isDefinedAndNotNull(affectedProcessor.activeThreadCount) && affectedProcessor.activeThreadCount > 0) {
+ return '(' + affectedProcessor.activeThreadCount + ')';
+ } else {
+ return '';
+ }
+ }).appendTo(affectedProcessorContainer);
+ };
+
+ /**
+ * Renders the specified affect controller service.
+ *
+ * @param {object} affectedControllerServiceEntity
+ * @param {jQuery} container
+ */
+ var renderAffectedControllerService = function (affectedControllerServiceEntity, container) {
+ var affectedControllerServiceContainer = $('<li class="affected-component-container"></li>').appendTo(container);
+ var affectedControllerService = affectedControllerServiceEntity.component;
+
+ // controller service state
+ $('<div class="referencing-component-state"></div>').addClass(function () {
+ if (nfCommon.isDefinedAndNotNull(affectedControllerService.state)) {
+ var icon = $(this);
+
+ var state = affectedControllerService.state === 'ENABLED' ? 'enabled' : 'disabled';
+ if (state === 'disabled' && !nfCommon.isEmpty(affectedControllerService.validationErrors)) {
+ state = 'invalid';
+
+ // build the error listing
+ var list = nfCommon.formatUnorderedList(affectedControllerService.validationErrors);
+
+ // add tooltip for the warnings
+ icon.qtip($.extend({},
+ nfCanvasUtils.config.systemTooltipConfig,
+ {
+ content: list
+ }));
+ }
+ return state;
+ } else {
+ return '';
+ }
+ }).appendTo(affectedControllerServiceContainer);
+
+ // bulletin
+ $('<div class="referencing-component-bulletins"></div>').addClass(affectedControllerService.id + '-affected-bulletins').appendTo(affectedControllerServiceContainer);
+
+ // controller service name
+ $('<span class="link"></span>').text(affectedControllerService.name).on('click', function () {
+ // check if there are outstanding changes
+ handleOutstandingChanges().done(function () {
+ // show the component in question
+ nfProcessGroupConfiguration.showConfiguration(affectedControllerService.processGroupId).done(function () {
+ nfProcessGroupConfiguration.selectControllerService(affectedControllerService.id);
+ });
+ });
+ }).appendTo(affectedControllerServiceContainer);
+ };
+
+ /**
+ * Populates the affected components for the specified variable.
+ *
+ * @param {object} affectedComponents
+ */
+ var populateAffectedComponents = function (affectedComponents) {
+ var affectedProcessors = [];
+ var affectedControllerServices = [];
+ var unauthorizedAffectedComponents = [];
+
+ // clear the affected components from the previous selection
+ var processorContainer = $('#variable-registry-affected-processors');
+ nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-state');
+ nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-bulletins');
+ processorContainer.empty();
+
+ var controllerServiceContainer = $('#variable-registry-affected-controller-services');
+ nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-state');
+ nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-bulletins');
+ controllerServiceContainer.empty();
+
+ var unauthorizedComponentsContainer = $('#variable-registry-affected-unauthorized-components').empty();
+
+ // affected component will be undefined when a new variable is added
+ if (nfCommon.isUndefined(affectedComponents)) {
+ $('<li class="affected-component-container"><span class="unset">Pending Apply</span></li>').appendTo(processorContainer);
+ $('<li class="affected-component-container"><span class="unset">Pending Apply</span></li>').appendTo(controllerServiceContainer);
+ $('<li class="affected-component-container"><span class="unset">Pending Apply</span></li>').appendTo(unauthorizedComponentsContainer);
+ } else {
+ var referencingComponentsForBulletinRetrieval = [];
+
+ // bin the affected components according to their type
+ $.each(affectedComponents, function (_, affectedComponentEntity) {
+ if (affectedComponentEntity.permissions.canRead === true && affectedComponentEntity.permissions.canWrite === true) {
+ referencingComponentsForBulletinRetrieval.push(affectedComponentEntity.id);
+
+ if (affectedComponentEntity.component.referenceType === 'PROCESSOR') {
+ affectedProcessors.push(affectedComponentEntity);
+ } else {
+ affectedControllerServices.push(affectedComponentEntity);
+ }
+ } else {
+ // if we're unauthorized only because the user is lacking write permissions, we can still query for bulletins
+ if (affectedComponentEntity.permissions.canRead === true) {
+ referencingComponentsForBulletinRetrieval.push(affectedComponentEntity.id);
+ }
+
+ unauthorizedAffectedComponents.push(affectedComponentEntity);
+ }
+ });
+
+ if (affectedProcessors.length === 0) {
+ $('<li class="affected-component-container"><span class="unset">None</span></li>').appendTo(processorContainer);
+ } else {
+ // sort the affected processors
+ affectedProcessors.sort(nameComparator);
+
+ // render each and register a click handler
+ $.each(affectedProcessors, function (_, affectedProcessorEntity) {
+ renderAffectedProcessor(affectedProcessorEntity, processorContainer);
+ });
+ }
+
+ if (affectedControllerServices.length === 0) {
+ $('<li class="affected-component-container"><span class="unset">None</span></li>').appendTo(controllerServiceContainer);
+ } else {
+ // sort the affected controller services
+ affectedControllerServices.sort(nameComparator);
+
+ // render each and register a click handler
+ $.each(affectedControllerServices, function (_, affectedControllerServiceEntity) {
+ renderAffectedControllerService(affectedControllerServiceEntity, controllerServiceContainer);
+ });
+ }
+
+ if (unauthorizedAffectedComponents.length === 0) {
+ $('<li class="affected-component-container"><span class="unset">None</span></li>').appendTo(unauthorizedComponentsContainer);
+ } else {
+ // sort the unauthorized affected components
+ unauthorizedAffectedComponents.sort(function (a, b) {
+ if (a.permissions.canRead === true && b.permissions.canRead === true) {
+ // processors before controller services
+ var sortVal = a.component.referenceType === b.component.referenceType ? 0 : a.component.referenceType > b.component.referenceType ? -1 : 1;
+
+ // if a and b are the same type, then sort by name
+ if (sortVal === 0) {
+ sortVal = a.component.name === b.component.name ? 0 : a.component.name > b.component.name ? 1 : -1;
+ }
+
+ return sortVal;
+ } else {
+
+ // if lacking read and write perms on both, sort by id
+ if (a.permissions.canRead === false && b.permissions.canRead === false) {
+ return a.id > b.id ? 1 : -1;
+ } else {
+ // if only one has read perms, then let it come first
+ if (a.permissions.canRead === true) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ }
+ });
+
+ $.each(unauthorizedAffectedComponents, function (_, unauthorizedAffectedComponentEntity) {
+ if (unauthorizedAffectedComponentEntity.permissions.canRead === true) {
+ if (unauthorizedAffectedComponentEntity.component.referenceType === 'PROCESSOR') {
+ renderAffectedProcessor(unauthorizedAffectedComponentEntity, unauthorizedComponentsContainer);
+ } else {
+ renderAffectedControllerService(unauthorizedAffectedComponentEntity, unauthorizedComponentsContainer);
+ }
+ } else {
+ var affectedUnauthorizedComponentContainer = $('<li class="affected-component-container"></li>').appendTo(unauthorizedComponentsContainer);
+ $('<span class="unset"></span>').text(unauthorizedAffectedComponentEntity.id).appendTo(affectedUnauthorizedComponentContainer);
+ }
+ });
+ }
+
+ // query for the bulletins
+ if (referencingComponentsForBulletinRetrieval.length > 0) {
+ nfCanvasUtils.queryBulletins(referencingComponentsForBulletinRetrieval).done(function (response) {
+ var bulletins = response.bulletinBoard.bulletins;
+
+ var bulletinsBySource = d3.nest()
+ .key(function (d) {
+ return d.sourceId;
+ })
+ .map(bulletins, d3.map);
+
+ bulletinsBySource.forEach(function (sourceId, sourceBulletins) {
+ $('div.' + sourceId + '-affected-bulletins').each(function () {
+ var bulletinIcon = $(this);
+
+ // if there are bulletins update them
+ if (sourceBulletins.length > 0) {
+ // format the new bulletins
+ var formattedBulletins = nfCommon.getFormattedBulletins(sourceBulletins);
+
+ var list = nfCommon.formatUnorderedList(formattedBulletins);
+
+ // update existing tooltip or initialize a new one if appropriate
+ bulletinIcon.addClass('has-bulletins').show().qtip($.extend({},
+ nfCanvasUtils.config.systemTooltipConfig,
+ {
+ content: list
+ }));
+ }
+ });
+ });
+ });
+ }
+ }
+ };
+
+ /**
+ * Shows the variable for the specified processGroupId.
+ *
+ * @param {string} processGroupId
+ * @param {string} variableToSelect to select
+ */
+ var showVariables = function (processGroupId, variableToSelect) {
+ return $.ajax({
+ type: 'GET',
+ url: '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/variable-registry',
+ dataType: 'json'
+ }).done(function (response) {
+ $('#process-group-variable-registry').text(getScopeLabel(processGroupId));
+ $('#variable-registry-process-group-id').text(processGroupId).data('revision', response.processGroupRevision);
+
+ // load the variables
+ loadVariables(response.variableRegistry, variableToSelect);
+
+ // show the dialog
+ $('#variable-registry-dialog').modal('show');
+ }).fail(nfErrorHandler.handleAjaxError);
+ };
+
+ /**
+ * Returns whether the currentVariable is overridden in the specified variables.
+ *
+ * @param {array} variables
+ * @param {object} currentVariable
+ * @returns {boolean} whether currentVariable is overridden
+ */
+ var isOverridden = function(variables, currentVariable) {
+ // identify any variables conflicting with the current variable
+ var conflictingVariables = [];
+ $.each(variables, function (_, variable) {
+ if (currentVariable.name === variable.name && variable.hidden === false) {
+ conflictingVariables.push(variable);
+ }
+ });
+
+ var isOverridden = false;
+
+ // if there are any variables conflicting
+ if (conflictingVariables.length > 1) {
+ var ancestry = [];
+
+ // get the breadcrumbs to walk the ancestry
+ var breadcrumbs = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
+ $.each(breadcrumbs, function (_, breadcrumbEntity) {
+ ancestry.push(breadcrumbEntity.id);
+ });
+
+ // check to see if the current process group is not part of the ancestry
+ var currentProcessGroupId = $('#variable-registry-process-group-id').text();
+ if (ancestry.indexOf(currentProcessGroupId) === -1) {
+ ancestry.push(currentProcessGroupId);
+ }
+
+ // go through each group in the ancestry
+ $.each(ancestry, function (_, processGroupId) {
+ // for each breadcrumb go through each variable
+ for (var i = 0; i < conflictingVariables.length; i++) {
+
+ // if this breadcrumb represents the process group for the conflicting variable
+ if (processGroupId === conflictingVariables[i].processGroupId) {
+
+ // if this conflicting variable is the current variable, mark as overridden as we
+ // know there is at least one more conflicting variable
+ if (currentVariable === conflictingVariables[i]) {
+ isOverridden = true;
+ }
+
+ conflictingVariables.splice(i, 1);
+
+ // if we are left with only a single variable break out of the breadcrumb iteration
+ if (conflictingVariables.length === 1) {
+ return false;
+ }
+ }
+ }
+ });
+ }
+
+ return isOverridden;
+ };
+
+ /**
+ * Returns whether the specified variable is editable.
+ *
+ * @param {object} variable
+ * @returns {boolean} if variable is editable
+ */
+ var isEditable = function (variable) {
+ // if the variable can be written based on the perms of the affected components
+ if (variable.canWrite === true) {
+ var currentProcessGroupId = $('#variable-registry-process-group-id').text();
+
+ // only support configuration if the variable belongs to the current group
+ if (variable.processGroupId === currentProcessGroupId) {
+
+ // verify the permissions of the group
+ var selection = nfCanvasUtils.getSelection();
+ if (selection.empty() === false && nf.CanvasUtils.isProcessGroup(selection)) {
+ var selectedData = selection.datum();
+ if (selectedData.id === currentProcessGroupId) {
+ return selectedData.permissions.canWrite === true;
+ }
+ }
+
+ var canWrite = false;
+ var breadcrumbs = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
+ $.each(breadcrumbs, function (_, breadcrumbEntity) {
+ if (breadcrumbEntity.id === currentProcessGroupId) {
+ canWrite = breadcrumbEntity.permissions.canWrite === true;
+ return false;
+ }
+ });
+
+ return canWrite;
+ }
+ }
+
+ return false;
+ };
+
+ /**
+ * Loads the specified variable registry.
+ *
+ * @param {object} variableRegistry
+ * @param {string} variableToSelect to select
+ */
+ var loadVariables = function (variableRegistry, variableToSelect) {
+ if (nfCommon.isDefinedAndNotNull(variableRegistry)) {
+ var count = 0;
+ var index = 0;
+
+ var variableGrid = $('#variable-registry-table').data('gridInstance');
+ var variableData = variableGrid.getData();
+
+ // begin the update
+ variableData.beginUpdate();
+
+ var variables = [];
+ $.each(variableRegistry.variables, function (i, variableEntity) {
+ var variable = variableEntity.variable;
+ variables.push({
+ id: count++,
+ hidden: false,
+ canWrite: variableEntity.canWrite,
+ name: variable.name,
+ value: variable.value,
+ previousValue: variable.value,
+ processGroupId: variable.processGroupId,
+ affectedComponents: variable.affectedComponents
+ });
+ });
+
+ $.each(variables, function (i, variable) {
+ variableData.addItem($.extend({
+ isOverridden: isOverridden(variables, variable),
+ isEditable: isEditable(variable)
+ }, variable));
+ });
+
+ // complete the update
+ variableData.endUpdate();
+ variableData.reSort();
+
+ // if we are pre-selecting a specific variable, get it's index
+ if (nfCommon.isDefinedAndNotNull(variableToSelect)) {
+ $.each(variables, function (i, variable) {
+ if (variableRegistry.processGroupId === variable.processGroupId && variable.name === variableToSelect) {
+ index = variableData.getRowById(variable.id);
+ }
+ });
+ }
+
+ if (variables.length === 0) {
+ // empty the containers
+ var processorContainer = $('#variable-registry-affected-processors');
+ nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-state');
+ nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-bulletins');
+ processorContainer.empty();
+
+ var controllerServiceContainer = $('#variable-registry-affected-controller-services');
+ nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-state');
+ nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-bulletins');
+ controllerServiceContainer.empty();
+
+ var unauthorizedComponentsContainer = $('#variable-registry-affected-unauthorized-components').empty();
+
+ // indicate no affected components
+ $('<li class="affected-component-container"><span class="unset">None</span></li>').appendTo(processorContainer);
+ $('<li class="affected-component-container"><span class="unset">None</span></li>').appendTo(controllerServiceContainer);
+ $('<li class="affected-component-container"><span class="unset">None</span></li>').appendTo(unauthorizedComponentsContainer);
+
+ // update the selection context
+ $('#affected-components-context').addClass('unset').text('None');
+ } else {
+ // select the desired row
+ variableGrid.setSelectedRows([index]);
+ }
+ }
+ };
+
+ /**
+ * Populates the variable update steps.
+ *
+ * @param {array} updateSteps
+ * @param {boolean} whether this request has been cancelled
+ * @param {boolean} whether this request has errored
+ */
+ var populateVariableUpdateStep = function (updateSteps, cancelled, errored) {
+ var updateStatusContainer = $('#variable-update-steps').empty();
+
+ // go through each step
+ $.each(updateSteps, function (_, updateStep) {
+ var stepItem = $('<li></li>').text(updateStep.description).appendTo(updateStatusContainer);
+
+ $('<div class="variable-step"></div>').addClass(function () {
+ if (nfCommon.isDefinedAndNotNull(updateStep.failureReason)) {
+ return 'ajax-error';
+ } else {
+ if (updateStep.complete === true) {
+ return 'ajax-complete';
+ } else {
+ return cancelled === true || errored === true ? 'ajax-error' : 'ajax-loading';
+ }
+ }
+ }).appendTo(stepItem);
+
+ $('<div class="clear"></div>').appendTo(stepItem);
+ });
+ };
+
+ /**
+ * Updates variables by issuing an update request and polling until it's completion.
+ */
+ var updateVariables = function () {
+ var variables = marshalVariables();
+ if (variables.length === 0) {
+ close();
+ return;
+ }
+
+ // update the variables context
+ var variableNames = variables.map(function (v) {
+ return v.variable.name;
+ });
+ $('#affected-components-context').removeClass('unset').text(variableNames.join(', '));
+
+ // get the current group id
+ var processGroupId = $('#variable-registry-process-group-id').text();
+
+ return $.Deferred(function (deferred) {
+ // updates the button model to show the close button
+ var updateToCloseButtonModel = function () {
+ $('#variable-registry-dialog').modal('setButtonModel', [{
+ buttonText: 'Close',
+ color: {
+ base: '#728E9B',
+ hover: '#004849',
+ text: '#ffffff'
+ },
+ handler: {
+ click: function () {
+ deferred.resolve();
+ close();
+ }
+ }
+ }]);
+ };
+
+ var cancelled = false;
+
+ // update the button model to show the cancel button
+ $('#variable-registry-dialog').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(processGroupId, requestId);
+ }
+
+ // update the step status
+ $('#variable-update-steps').find('div.variable-step.ajax-loading').removeClass('ajax-loading').addClass('ajax-error');
+
+ // update the button model
+ updateToCloseButtonModel();
+ };
+
+ submitUpdateRequest(processGroupId, variables).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 components
+ populateAffectedComponents(updateRequest.affectedComponents);
+
+ // update the progress/steps
+ populateVariableUpdateStep(updateRequest.updateSteps, cancelled, errored);
+
+ // if this request was cancelled, remove the update request
+ if (cancelled) {
+ deleteUpdateRequest(updateRequest.processGroupId, requestId);
+ } else {
+ if (updateRequest.complete === true) {
+ if (errored) {
+ nfDialog.showOkDialog({
+ headerText: 'Variable Update Error',
+ dialogContent: 'Unable to complete variable update request: ' + nfCommon.escapeHtml(updateRequest.failureReason)
+ });
+ }
+
+ // reload affected processors
+ $.each(updateRequest.affectedComponents, function (_, affectedComponentEntity) {
+ if (affectedComponentEntity.permissions.canRead === true) {
+ var affectedComponent = affectedComponentEntity.component;
+
+ // reload the process if it's in the current group
+ if (affectedComponent.referenceType === 'PROCESSOR' && nfCanvasUtils.getGroupId() === affectedComponent.processGroupId) {
+ nfProcessor.reload(affectedComponent.id);
+ }
+ }
+ });
+
+ // reload the process group if the context of the update is not the current group (meaning its a child of the current group)
+ if (nfCanvasUtils.getGroupId() !== updateRequest.processGroupId) {
+ nfProcessGroup.reload(updateRequest.processGroupId);
+ }
+
+ // delete the update request
+ deleteUpdateRequest(updateRequest.processGroupId, requestId);
+
+ // update the button model
+ updateToCloseButtonModel();
+ } else {
+ // wait to get an updated status
+ setTimeout(function () {
+ getUpdateRequest(updateRequest.processGroupId, requestId).done(function (getResponse) {
+ pollUpdateRequest(getResponse);
+ }).fail(handleAjaxFailure);
+ }, 2000);
+ }
+ }
+ };
+
+ // update the visibility
+ $('#variable-registry-table, #add-variable').hide();
+ $('#variable-update-status').show();
+
+ pollUpdateRequest(response);
+ }).fail(handleAjaxFailure);
+ }).promise();
+ };
+
+ /**
+ * Submits an variable update request.
+ *
+ * @param {string} processGroupId
+ * @param {object} variables
+ * @returns {deferred} update request xhr
+ */
+ var submitUpdateRequest = function (processGroupId, variables) {
+ var processGroupRevision = $('#variable-registry-process-group-id').data('revision');
+
+ var updateRequestEntity = {
+ processGroupRevision: nfClient.getRevision({
+ revision: {
+ version: processGroupRevision.version
+ }
+ }),
+ variableRegistry: {
+ processGroupId: processGroupId,
+ variables: variables
+ }
+ };
+
+ return $.ajax({
+ type: 'POST',
+ data: JSON.stringify(updateRequestEntity),
+ url: '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/variable-registry/update-requests',
+ dataType: 'json',
+ contentType: 'application/json'
+ }).fail(nfErrorHandler.handleAjaxError);
+ };
+
+ /**
+ * Obtains the current state of the updateRequest using the specified process group id and update request id.
+ *
+ * @param {string} processGroupId
+ * @param {string} updateRequestId
+ * @returns {deferred} update request xhr
+ */
+ var getUpdateRequest = function (processGroupId, updateRequestId) {
+ return $.ajax({
+ type: 'GET',
+ url: '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/variable-registry/update-requests/' + encodeURIComponent(updateRequestId),
+ dataType: 'json'
+ }).fail(nfErrorHandler.handleAjaxError);
+ };
+
+ /**
+ * Deletes an updateRequest using the specified process group id and update request id.
+ *
+ * @param {string} processGroupId
+ * @param {string} updateRequestId
+ * @returns {deferred} update request xhr
+ */
+ var deleteUpdateRequest = function (processGroupId, updateRequestId) {
+ return $.ajax({
+ type: 'DELETE',
+ url: '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/variable-registry/update-requests/' + encodeURIComponent(updateRequestId),
+ dataType: 'json'
+ }).fail(nfErrorHandler.handleAjaxError);
+ };
+
+ /**
+ * Marshals the variables in the table.
+ */
+ var marshalVariables = function () {
+ var variables = [];
+
+ var variableGrid = $('#variable-registry-table').data('gridInstance');
+ var variableData = variableGrid.getData();
+
+ $.each(variableData.getItems(), function () {
+ var variable = {
+ 'name': this.name
+ };
+
+ var modified = false;
+ if (this.hidden === true && this.previousValue !== null) {
+ // hidden variables were removed by the user, clear the value
+ variable['value'] = null;
+ modified = true;
+ } else if (this.value !== this.previousValue) {
+ // the value has changed
+ variable['value'] = this.value;
+ modified = true;
+ }
+
+ if (modified) {
+ variables.push({
+ 'variable': variable
+ });
+ }
+ });
+
+ return variables;
+ };
+
+ /**
+ * Adds a new variable.
+ */
+ var addNewVariable = function () {
+ var currentProcessGroupId = $('#variable-registry-process-group-id').text();
+ var variableName = $.trim($('#new-variable-name').val());
+
+ // ensure the property name is specified
+ if (variableName !== '') {
+ var variableGrid = $('#variable-registry-table').data('gridInstance');
+ var variableData = variableGrid.getData();
+
+ // ensure the property name is unique
+ var conflictingVariables = [];
+ var matchingVariable = null;
+ $.each(variableData.getItems(), function (_, item) {
+ if (variableName === item.name) {
+ // if the scope is same, this is an exact match otherwise we've identified a conflicting variable
+ if (currentProcessGroupId === item.processGroupId) {
+ matchingVariable = item;
+ } else {
+ conflictingVariables.push(item);
+ }
+ }
+ });
+
+ if (matchingVariable === null) {
+ // add a row for the new variable
+ var id = variableData.getLength();
+ variableData.addItem({
+ id: id,
+ hidden: false,
+ canWrite: true,
+ name: variableName,
+ value: null,
+ previousValue: null,
+ processGroupId: currentProcessGroupId,
+ isEditable: true,
+ isOverridden: false
+ });
+
+ // we've just added a new variable, mark any conflicting variables as overridden
+ $.each(conflictingVariables, function (_, conflictingVariable) {
+ variableData.updateItem(conflictingVariable.id, $.extend(conflictingVariable, {
+ isOverridden: true
+ }));
+ });
+
+ // sort the data
+ variableData.reSort();
+
+ // select the new variable row
+ var row = variableData.getRowById(id);
+ variableGrid.setActiveCell(row, variableGrid.getColumnIndex('value'));
+ variableGrid.editActiveCell();
+ } else {
+ // if this row is currently hidden, clear the value and show it
+ if (matchingVariable.hidden === true) {
+ variableData.updateItem(matchingVariable.id, $.extend(matchingVariable, {
+ hidden: false,
+ value: null
+ }));
+
+ // select the new properties row
+ var editableMatchingRow = variableData.getRowById(matchingVariable.id);
+ variableGrid.setActiveCell(editableMatchingRow, variableGrid.getColumnIndex('value'));
+ variableGrid.editActiveCell();
+ } else {
+ nfDialog.showOkDialog({
+ headerText: 'Variable Exists',
+ dialogContent: 'A variable with this name already exists.'
+ });
+
+ // select the existing properties row
+ var matchingRow = variableData.getRowById(matchingVariable.id);
+ variableGrid.setSelectedRows([matchingRow]);
+ variableGrid.scrollRowIntoView(matchingRow);
+ }
+ }
+ } else {
+ nfDialog.showOkDialog({
+ headerText: 'Variable Name',
+ dialogContent: 'Variable name must be specified.'
+ });
+ }
+
+ // close the new variable dialog
+ $('#new-variable-dialog').modal('hide');
+ };
+
+ /**
+ * Cancels adding a new variable.
+ */
+ var close = function () {
+ $('#variable-registry-dialog').modal('hide');
+ };
+
+ /**
+ * Reset the dialog.
+ */
+ var resetDialog = function () {
+ $('#variable-registry-table, #add-variable').show();
+ $('#variable-update-status').hide();
+
+ $('#process-group-variable-registry').text('');
+ $('#variable-registry-process-group-id').text('').removeData('revision');
+ $('#affected-components-context').removeClass('unset').text('');
+
+ var variableGrid = $('#variable-registry-table').data('gridInstance');
+ var variableData = variableGrid.getData();
+ variableData.setItems([]);
+
+ var affectedProcessorContainer = $('#variable-registry-affected-processors');
+ nfCommon.cleanUpTooltips(affectedProcessorContainer, 'div.referencing-component-state');
+ nfCommon.cleanUpTooltips(affectedProcessorContainer, 'div.referencing-component-bulletins');
+ affectedProcessorContainer.empty();
+
+ var affectedControllerServicesContainer = $('#variable-registry-affected-controller-services');
+ nfCommon.cleanUpTooltips(affectedControllerServicesContainer, 'div.referencing-component-state');
+ nfCommon.cleanUpTooltips(affectedControllerServicesContainer, 'div.referencing-component-bulletins');
+ affectedControllerServicesContainer.empty();
+
+ $('#variable-registry-affected-unauthorized-components').empty();
+ };
+
+ return {
+ /**
+ * Initializes the variable registry dialogs.
+ */
+ init: function () {
+ $('#variable-registry-dialog').modal({
+ scrollableContentStyle: 'scrollable',
+ headerText: 'Variables',
+ handler: {
+ close: function () {
+ resetDialog();
+ },
+ open: function () {
+ var variableGrid = $('#variable-registry-table').data('gridInstance');
+ if (nfCommon.isDefinedAndNotNull(variableGrid)) {
+ variableGrid.resizeCanvas();
+ }
+ }
+ }
+ });
+
+ $('#new-variable-dialog').modal({
+ headerText: 'New Variable',
+ buttons: [{
+ buttonText: 'Ok',
+ color: {
+ base: '#728E9B',
+ hover: '#004849',
+ text: '#ffffff'
+ },
+ handler: {
+ click: function () {
+ addNewVariable();
+ }
+ }
+ }],
+ handler: {
+ close: function () {
+ $('#new-variable-name').val('');
+ },
+ open: function () {
+ $('#new-variable-name').focus();
+ }
+ }
+ });
+
+ $('#new-variable-name').on('keydown', function (e) {
+ var code = e.keyCode ? e.keyCode : e.which;
+ if (code === $.ui.keyCode.ENTER) {
+ addNewVariable();
+
+ // prevents the enter from propagating into the field for editing the new property value
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ }
+ });
+
+ $('#add-variable').on('click', function () {
+ $('#new-variable-dialog').modal('show');
+ });
+
+ initVariableTable();
+ },
+
+ /**
+ * Shows the variables for the specified process group.
+ *
+ * @param {string} processGroupId
+ */
+ showVariables: function (processGroupId) {
+ // restore the button model
+ $('#variable-registry-dialog').modal('setButtonModel', [{
+ buttonText: 'Apply',
+ color: {
+ base: '#728E9B',
+ hover: '#004849',
+ text: '#ffffff'
+ },
+ handler: {
+ click: function () {
+ updateVariables();
+ }
+ }
+ }, {
+ buttonText: 'Cancel',
+ color: {
+ base: '#E3E8EB',
+ hover: '#C7D2D7',
+ text: '#004849'
+ },
+ handler: {
+ click: function () {
+ close();
+ }
+ }
+ }]);
+
+ return showVariables(processGroupId);
+ }
+ };
+}));
\ No newline at end of file
[3/4] nifi git commit: NIFI-4280: - Adding support for the user to
configure variables in the UI. - Updating the endpoints for changing
variables as necessary. This closes #2135.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
index 0b634a1..0e574e0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
@@ -16,56 +16,18 @@
*/
package org.apache.nifi.web.api;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.GET;
-import javax.ws.rs.HttpMethod;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.transform.stream.StreamSource;
-
+import com.sun.jersey.api.core.ResourceContext;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import com.sun.jersey.multipart.FormDataParam;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+import com.wordnik.swagger.annotations.Authorization;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.AuthorizeAccess;
import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ComponentAuthorizable;
@@ -75,19 +37,18 @@ import org.apache.nifi.authorization.SnippetAuthorizable;
import org.apache.nifi.authorization.TemplateContentsAuthorizable;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserDetails;
import org.apache.nifi.authorization.user.NiFiUserUtils;
import org.apache.nifi.bundle.BundleCoordinate;
+import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.connectable.ConnectableType;
import org.apache.nifi.controller.ScheduledState;
import org.apache.nifi.controller.serialization.FlowEncodingVersion;
import org.apache.nifi.controller.service.ControllerServiceState;
-import org.apache.nifi.framework.security.util.SslContextFactory;
import org.apache.nifi.registry.variable.VariableRegistryUpdateRequest;
import org.apache.nifi.registry.variable.VariableRegistryUpdateStep;
import org.apache.nifi.remote.util.SiteToSiteRestApiClient;
import org.apache.nifi.util.BundleUtils;
-import org.apache.nifi.util.FormatUtils;
-import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.ResourceNotFoundException;
import org.apache.nifi.web.Revision;
@@ -106,9 +67,9 @@ import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.TemplateDTO;
import org.apache.nifi.web.api.dto.VariableRegistryDTO;
import org.apache.nifi.web.api.dto.flow.FlowDTO;
-import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO;
-import org.apache.nifi.web.api.dto.status.ProcessGroupStatusSnapshotDTO;
+import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import org.apache.nifi.web.api.entity.ConnectionEntity;
import org.apache.nifi.web.api.entity.ConnectionsEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity;
@@ -125,8 +86,6 @@ import org.apache.nifi.web.api.entity.LabelsEntity;
import org.apache.nifi.web.api.entity.OutputPortsEntity;
import org.apache.nifi.web.api.entity.PortEntity;
import org.apache.nifi.web.api.entity.ProcessGroupEntity;
-import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity;
-import org.apache.nifi.web.api.entity.ProcessGroupStatusSnapshotEntity;
import org.apache.nifi.web.api.entity.ProcessGroupsEntity;
import org.apache.nifi.web.api.entity.ProcessorEntity;
import org.apache.nifi.web.api.entity.ProcessorsEntity;
@@ -138,23 +97,61 @@ import org.apache.nifi.web.api.entity.VariableRegistryEntity;
import org.apache.nifi.web.api.entity.VariableRegistryUpdateRequestEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter;
+import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
import org.apache.nifi.web.util.Pause;
-import org.apache.nifi.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.config.ClientConfig;
-import com.sun.jersey.api.client.config.DefaultClientConfig;
-import com.sun.jersey.api.core.ResourceContext;
-import com.sun.jersey.multipart.FormDataParam;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiParam;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
-import com.wordnik.swagger.annotations.Authorization;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* RESTful endpoint for managing a Group.
@@ -455,14 +452,12 @@ public class ProcessGroupResource extends ApplicationResource {
throw new IllegalArgumentException("Group ID and Update ID must both be specified.");
}
- if (isReplicateRequest()) {
- return replicate(HttpMethod.GET);
- }
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
- processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
+ processGroup.authorize(authorizer, RequestAction.READ, user);
});
final VariableRegistryUpdateRequest request = varRegistryUpdateRequests.get(updateId);
@@ -474,9 +469,14 @@ public class ProcessGroupResource extends ApplicationResource {
throw new ResourceNotFoundException("Could not find a Variable Registry Update Request with identifier " + updateId + " for Process Group with identifier " + groupId);
}
+ if (!user.equals(request.getUser())) {
+ throw new IllegalArgumentException("Only the user that submitted the update request can retrieve it.");
+ }
+
final VariableRegistryUpdateRequestEntity entity = new VariableRegistryUpdateRequestEntity();
- entity.setRequestDto(dtoFactory.createVariableRegistryUpdateRequestDto(request));
- entity.getRequestDto().setUri(generateResourceUri("process-groups", groupId, "variable-registry", updateId));
+ entity.setRequest(dtoFactory.createVariableRegistryUpdateRequestDto(request));
+ entity.setProcessGroupRevision(request.getProcessGroupRevision());
+ entity.getRequest().setUri(generateResourceUri("process-groups", groupId, "variable-registry", updateId));
return generateOkResponse(entity).build();
}
@@ -506,15 +506,13 @@ public class ProcessGroupResource extends ApplicationResource {
throw new IllegalArgumentException("Group ID and Update ID must both be specified.");
}
- if (isReplicateRequest()) {
- return replicate(HttpMethod.DELETE);
- }
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
- processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
- processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+ processGroup.authorize(authorizer, RequestAction.READ, user);
+ processGroup.authorize(authorizer, RequestAction.WRITE, user);
});
final VariableRegistryUpdateRequest request = varRegistryUpdateRequests.remove(updateId);
@@ -526,11 +524,16 @@ public class ProcessGroupResource extends ApplicationResource {
throw new ResourceNotFoundException("Could not find a Variable Registry Update Request with identifier " + updateId + " for Process Group with identifier " + groupId);
}
+ if (!user.equals(request.getUser())) {
+ throw new IllegalArgumentException("Only the user that submitted the update request can remove it.");
+ }
+
request.cancel();
final VariableRegistryUpdateRequestEntity entity = new VariableRegistryUpdateRequestEntity();
- entity.setRequestDto(dtoFactory.createVariableRegistryUpdateRequestDto(request));
- entity.getRequestDto().setUri(generateResourceUri("process-groups", groupId, "variable-registry", updateId));
+ entity.setRequest(dtoFactory.createVariableRegistryUpdateRequestDto(request));
+ entity.setProcessGroupRevision(request.getProcessGroupRevision());
+ entity.getRequest().setUri(generateResourceUri("process-groups", groupId, "variable-registry", updateId));
return generateOkResponse(entity).build();
}
@@ -634,11 +637,11 @@ public class ProcessGroupResource extends ApplicationResource {
// 1. Determine Affected Components (this includes any Processors and Controller Services and any components that reference an affected Controller Service).
// 1a. Determine ID's of components
// 1b. Determine Revision's of associated components
- // 2. Stop All Affected Processors
- // 3. Disable All Affected Controller Services
+ // 2. Stop All Active Affected Processors
+ // 3. Disable All Active Affected Controller Services
// 4. Update the Variables
- // 5. Re-Enable all Affected Controller Services (services only, not dependent components)
- // 6. Re-Enable all Processors that Depended on the Controller Services
+ // 5. Re-Enable all previously Active Affected Controller Services (services only, not dependent components)
+ // 6. Re-Enable all previously Active Processors that Depended on the Controller Services
// Determine the affected components (and their associated revisions)
final VariableRegistryEntity computedEntity = serviceFacade.populateAffectedComponents(requestEntity.getVariableRegistry());
@@ -647,38 +650,77 @@ public class ProcessGroupResource extends ApplicationResource {
throw new ResourceNotFoundException(String.format("Unable to locate group with id '%s'.", groupId));
}
- final Set<AffectedComponentDTO> affectedComponents = serviceFacade.getComponentsAffectedByVariableRegistryUpdate(requestEntity.getVariableRegistry());
+ final Set<AffectedComponentEntity> allAffectedComponents = serviceFacade.getComponentsAffectedByVariableRegistryUpdate(requestEntity.getVariableRegistry());
+ final Set<AffectedComponentDTO> activeAffectedComponents = serviceFacade.getActiveComponentsAffectedByVariableRegistryUpdate(requestEntity.getVariableRegistry());
- final Map<String, List<AffectedComponentDTO>> affectedComponentsByType = affectedComponents.stream()
- .collect(Collectors.groupingBy(comp -> comp.getComponentType()));
+ final Map<String, List<AffectedComponentDTO>> activeAffectedComponentsByType = activeAffectedComponents.stream()
+ .collect(Collectors.groupingBy(comp -> comp.getReferenceType()));
- final List<AffectedComponentDTO> affectedProcessors = affectedComponentsByType.get(AffectedComponentDTO.COMPONENT_TYPE_PROCESSOR);
- final List<AffectedComponentDTO> affectedServices = affectedComponentsByType.get(AffectedComponentDTO.COMPONENT_TYPE_CONTROLLER_SERVICE);
+ final List<AffectedComponentDTO> activeAffectedProcessors = activeAffectedComponentsByType.get(AffectedComponentDTO.COMPONENT_TYPE_PROCESSOR);
+ final List<AffectedComponentDTO> activeAffectedServices = activeAffectedComponentsByType.get(AffectedComponentDTO.COMPONENT_TYPE_CONTROLLER_SERVICE);
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+ // define access authorize for execution below
+ final AuthorizeAccess authorizeAccess = lookup -> {
+ final Authorizable groupAuthorizable = lookup.getProcessGroup(groupId).getAuthorizable();
+ groupAuthorizable.authorize(authorizer, RequestAction.WRITE, user);
+
+ // For every component that is affected, the user must have READ permissions and WRITE permissions
+ // (because this action requires stopping the component).
+ if (activeAffectedProcessors != null) {
+ for (final AffectedComponentDTO activeAffectedComponent : activeAffectedProcessors) {
+ final Authorizable authorizable = lookup.getProcessor(activeAffectedComponent.getId()).getAuthorizable();
+ authorizable.authorize(authorizer, RequestAction.READ, user);
+ authorizable.authorize(authorizer, RequestAction.WRITE, user);
+ }
+ }
+
+ if (activeAffectedServices != null) {
+ for (final AffectedComponentDTO activeAffectedComponent : activeAffectedServices) {
+ final Authorizable authorizable = lookup.getControllerService(activeAffectedComponent.getId()).getAuthorizable();
+ authorizable.authorize(authorizer, RequestAction.READ, user);
+ authorizable.authorize(authorizer, RequestAction.WRITE, user);
+ }
+ }
+ };
if (isReplicateRequest()) {
+ // authorize access
+ serviceFacade.authorizeAccess(authorizeAccess);
+
// update the variable registry
- final VariableRegistryUpdateRequest updateRequest = createVariableRegistryUpdateRequest(groupId);
+ final VariableRegistryUpdateRequest updateRequest = createVariableRegistryUpdateRequest(groupId, allAffectedComponents, user);
updateRequest.getIdentifyRelevantComponentsStep().setComplete(true);
final URI originalUri = getAbsolutePath();
// Submit the task to be run in the background
final Runnable taskWrapper = () -> {
try {
- updateVariableRegistryReplicated(groupId, originalUri, affectedProcessors, affectedServices, updateRequest, requestEntity);
+ // set the user authentication token
+ final Authentication authentication = new NiFiAuthenticationToken(new NiFiUserDetails(user));
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ updateVariableRegistryReplicated(groupId, originalUri, activeAffectedProcessors, activeAffectedServices, updateRequest, requestEntity);
} catch (final Exception e) {
logger.error("Failed to update variable registry", e);
+
+ updateRequest.setComplete(true);
updateRequest.setFailureReason("An unexpected error has occurred: " + e);
+ } finally {
+ // clear the authentication token
+ SecurityContextHolder.getContext().setAuthentication(null);
}
};
variableRegistryThreadPool.submit(taskWrapper);
final VariableRegistryUpdateRequestEntity responseEntity = new VariableRegistryUpdateRequestEntity();
- responseEntity.setRequestDto(dtoFactory.createVariableRegistryUpdateRequestDto(updateRequest));
- responseEntity.getRequestDto().setUri(generateResourceUri("process-groups", groupId, "variable-registry", "update-requests", updateRequest.getRequestId()));
+ responseEntity.setRequest(dtoFactory.createVariableRegistryUpdateRequestDto(updateRequest));
+ responseEntity.setProcessGroupRevision(updateRequest.getProcessGroupRevision());
+ responseEntity.getRequest().setUri(generateResourceUri("process-groups", groupId, "variable-registry", "update-requests", updateRequest.getRequestId()));
- final URI location = URI.create(responseEntity.getRequestDto().getUri());
+ final URI location = URI.create(responseEntity.getRequest().getUri());
return Response.status(Status.ACCEPTED).location(location).entity(responseEntity).build();
}
@@ -688,34 +730,10 @@ public class ProcessGroupResource extends ApplicationResource {
serviceFacade,
requestEntity,
requestRevision,
- lookup -> {
- final NiFiUser user = NiFiUserUtils.getNiFiUser();
-
- final Authorizable groupAuthorizable = lookup.getProcessGroup(groupId).getAuthorizable();
- groupAuthorizable.authorize(authorizer, RequestAction.WRITE, user);
-
- // For every component that is affected, the user must have READ permissions and WRITE permissions
- // (because this action requires stopping the component).
- if (affectedProcessors != null) {
- for (final AffectedComponentDTO affectedComponent : affectedProcessors) {
- final Authorizable authorizable = lookup.getProcessor(affectedComponent.getComponentId()).getAuthorizable();
- authorizable.authorize(authorizer, RequestAction.READ, user);
- authorizable.authorize(authorizer, RequestAction.WRITE, user);
- }
- }
-
- if (affectedServices != null) {
- for (final AffectedComponentDTO affectedComponent : affectedServices) {
- final Authorizable authorizable = lookup.getControllerService(affectedComponent.getComponentId()).getAuthorizable();
- authorizable.authorize(authorizer, RequestAction.READ, user);
- authorizable.authorize(authorizer, RequestAction.WRITE, user);
- }
- }
- },
+ authorizeAccess,
null,
- (revision, varRegistryEntity) -> {
- return updateVariableRegistryLocal(groupId, affectedProcessors, affectedServices, requestEntity);
- });
+ (revision, varRegistryEntity) -> updateVariableRegistryLocal(groupId, allAffectedComponents, activeAffectedProcessors, activeAffectedServices, user, requestEntity)
+ );
}
private Pause createPause(final VariableRegistryUpdateRequest updateRequest) {
@@ -739,58 +757,49 @@ public class ProcessGroupResource extends ApplicationResource {
}
private void updateVariableRegistryReplicated(final String groupId, final URI originalUri, final Collection<AffectedComponentDTO> affectedProcessors,
- final Collection<AffectedComponentDTO> affectedServices,
- final VariableRegistryUpdateRequest updateRequest, final VariableRegistryEntity requestEntity) {
-
- final NiFiProperties properties = getProperties();
- final Client jerseyClient = WebUtils.createClient(new DefaultClientConfig(), SslContextFactory.createSslContext(properties));
- final int connectionTimeout = (int) FormatUtils.getTimeDuration(properties.getClusterNodeConnectionTimeout(), TimeUnit.MILLISECONDS);
- final int readTimeout = (int) FormatUtils.getTimeDuration(properties.getClusterNodeReadTimeout(), TimeUnit.MILLISECONDS);
- jerseyClient.getProperties().put(ClientConfig.PROPERTY_CONNECT_TIMEOUT, connectionTimeout);
- jerseyClient.getProperties().put(ClientConfig.PROPERTY_READ_TIMEOUT, readTimeout);
- jerseyClient.getProperties().put(ClientConfig.PROPERTY_FOLLOW_REDIRECTS, Boolean.TRUE);
+ final Collection<AffectedComponentDTO> affectedServices, final VariableRegistryUpdateRequest updateRequest,
+ final VariableRegistryEntity requestEntity) throws InterruptedException, IOException {
final Pause pause = createPause(updateRequest);
// stop processors
if (affectedProcessors != null) {
- logger.info("In order to update Variable Registry for Process Group with ID {}, "
- + "replicating request to stop {} affected processors", groupId, affectedProcessors.size());
-
- scheduleProcessors(groupId, originalUri, jerseyClient, updateRequest, pause,
- affectedProcessors, ScheduledState.STOPPED, updateRequest.getStopProcessorsStep());
+ logger.info("In order to update Variable Registry for Process Group with ID {}, replicating request to stop {} affected Processors", groupId, affectedProcessors.size());
+ scheduleProcessors(groupId, originalUri, updateRequest, pause, affectedProcessors, ScheduledState.STOPPED, updateRequest.getStopProcessorsStep());
+ } else {
+ logger.info("In order to update Variable Registry for Process Group with ID {}, no Processors are affected.", groupId);
+ updateRequest.getStopProcessorsStep().setComplete(true);
}
// disable controller services
if (affectedServices != null) {
- logger.info("In order to update Variable Registry for Process Group with ID {}, "
- + "replicating request to stop {} affected Controller Services", groupId, affectedServices.size());
-
- activateControllerServices(groupId, originalUri, jerseyClient, updateRequest, pause,
- affectedServices, ControllerServiceState.DISABLED, updateRequest.getDisableServicesStep());
+ logger.info("In order to update Variable Registry for Process Group with ID {}, replicating request to stop {} affected Controller Services", groupId, affectedServices.size());
+ activateControllerServices(groupId, originalUri, updateRequest, pause, affectedServices, ControllerServiceState.DISABLED, updateRequest.getDisableServicesStep());
+ } else {
+ logger.info("In order to update Variable Registry for Process Group with ID {}, no Controller Services are affected.", groupId);
+ updateRequest.getDisableServicesStep().setComplete(true);
}
// apply updates
- logger.info("In order to update Variable Registry for Process Group with ID {}, "
- + "replicating request to apply updates to variable registry", groupId);
- applyVariableRegistryUpdate(groupId, originalUri, jerseyClient, updateRequest, requestEntity);
+ logger.info("In order to update Variable Registry for Process Group with ID {}, replicating request to apply updates to variable registry", groupId);
+ applyVariableRegistryUpdate(groupId, originalUri, updateRequest, requestEntity);
// re-enable controller services
if (affectedServices != null) {
- logger.info("In order to update Variable Registry for Process Group with ID {}, "
- + "replicating request to re-enable {} affected services", groupId, affectedServices.size());
-
- activateControllerServices(groupId, originalUri, jerseyClient, updateRequest, pause,
- affectedServices, ControllerServiceState.ENABLED, updateRequest.getEnableServicesStep());
+ logger.info("In order to update Variable Registry for Process Group with ID {}, replicating request to re-enable {} affected services", groupId, affectedServices.size());
+ activateControllerServices(groupId, originalUri, updateRequest, pause, affectedServices, ControllerServiceState.ENABLED, updateRequest.getEnableServicesStep());
+ } else {
+ logger.info("In order to update Variable Registry for Process Group with ID {}, no Controller Services are affected.", groupId);
+ updateRequest.getEnableServicesStep().setComplete(true);
}
// restart processors
if (affectedProcessors != null) {
- logger.info("In order to update Variable Registry for Process Group with ID {}, "
- + "replicating request to restart {} affected processors", groupId, affectedProcessors.size());
-
- scheduleProcessors(groupId, originalUri, jerseyClient, updateRequest, pause,
- affectedProcessors, ScheduledState.RUNNING, updateRequest.getStartProcessorsStep());
+ logger.info("In order to update Variable Registry for Process Group with ID {}, replicating request to restart {} affected processors", groupId, affectedProcessors.size());
+ scheduleProcessors(groupId, originalUri, updateRequest, pause, affectedProcessors, ScheduledState.RUNNING, updateRequest.getStartProcessorsStep());
+ } else {
+ logger.info("In order to update Variable Registry for Process Group with ID {}, no Processors are affected.", groupId);
+ updateRequest.getStartProcessorsStep().setComplete(true);
}
updateRequest.setComplete(true);
@@ -799,34 +808,45 @@ public class ProcessGroupResource extends ApplicationResource {
/**
* Periodically polls the process group with the given ID, waiting for all processors whose ID's are given to have the given Scheduled State.
*
- * @param client the Jersey Client to use for making the request
* @param groupId the ID of the Process Group to poll
* @param processorIds the ID of all Processors whose state should be equal to the given desired state
* @param desiredState the desired state for all processors with the ID's given
* @param pause the Pause that can be used to wait between polling
* @return <code>true</code> if successful, <code>false</code> if unable to wait for processors to reach the desired state
*/
- private boolean waitForProcessorStatus(final Client client, final URI originalUri, final String groupId, final Set<String> processorIds, final ScheduledState desiredState, final Pause pause) {
+ private boolean waitForProcessorStatus(final URI originalUri, final String groupId, final Set<String> processorIds, final ScheduledState desiredState,
+ final VariableRegistryUpdateRequest updateRequest, final Pause pause) throws InterruptedException {
URI groupUri;
try {
groupUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
- originalUri.getPort(), "/nifi-api/flow/process-groups/" + groupId + "/status", "recursive=true", originalUri.getFragment());
+ originalUri.getPort(), "/nifi-api/process-groups/" + groupId + "/processors", "includeDescendantGroups=true", originalUri.getFragment());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
+ final Map<String, String> headers = new HashMap<>();
+ final MultivaluedMap<String, String> requestEntity = new MultivaluedMapImpl();
+
boolean continuePolling = true;
while (continuePolling) {
- final ClientResponse response = client.resource(groupUri).header("Content-Type", "application/json").get(ClientResponse.class);
- if (response.getStatus() != Status.OK.getStatusCode()) {
+
+ // Determine whether we should replicate only to the cluster coordinator, or if we should replicate directly to the cluster nodes themselves.
+ final NodeResponse clusterResponse;
+ if (getReplicationTarget() == ReplicationTarget.CLUSTER_NODES) {
+ clusterResponse = getRequestReplicator().replicate(HttpMethod.GET, groupUri, requestEntity, headers).awaitMergedResponse();
+ } else {
+ clusterResponse = getRequestReplicator().forwardToCoordinator(
+ getClusterCoordinatorNode(), HttpMethod.GET, groupUri, requestEntity, headers).awaitMergedResponse();
+ }
+
+ if (clusterResponse.getStatus() != Status.OK.getStatusCode()) {
return false;
}
- final ProcessGroupStatusEntity statusEntity = response.getEntity(ProcessGroupStatusEntity.class);
- final ProcessGroupStatusDTO statusDto = statusEntity.getProcessGroupStatus();
- final ProcessGroupStatusSnapshotDTO statusSnapshotDto = statusDto.getAggregateSnapshot();
+ final ProcessorsEntity processorsEntity = getResponseEntity(clusterResponse, ProcessorsEntity.class);
+ final Set<ProcessorEntity> processorEntities = processorsEntity.getProcessors();
- if (isProcessorStatusEqual(statusSnapshotDto, processorIds, desiredState)) {
+ if (isProcessorActionComplete(processorEntities, updateRequest, processorIds, desiredState)) {
logger.debug("All {} processors of interest now have the desired state of {}", processorIds.size(), desiredState);
return true;
}
@@ -847,14 +867,14 @@ public class ProcessGroupResource extends ApplicationResource {
* @param pause the Pause that can be used to wait between polling
* @return <code>true</code> if successful, <code>false</code> if unable to wait for processors to reach the desired state
*/
- private boolean waitForLocalProcessorStatus(final String groupId, final Set<String> processorIds, final ScheduledState desiredState, final Pause pause) {
+ private boolean waitForLocalProcessor(final String groupId, final Set<String> processorIds, final ScheduledState desiredState,
+ final VariableRegistryUpdateRequest updateRequest, final Pause pause) {
+
boolean continuePolling = true;
while (continuePolling) {
- final ProcessGroupStatusEntity statusEntity = serviceFacade.getProcessGroupStatus(groupId, true);
- final ProcessGroupStatusDTO statusDto = statusEntity.getProcessGroupStatus();
- final ProcessGroupStatusSnapshotDTO statusSnapshotDto = statusDto.getAggregateSnapshot();
+ final Set<ProcessorEntity> processorEntities = serviceFacade.getProcessors(groupId, true);
- if (isProcessorStatusEqual(statusSnapshotDto, processorIds, desiredState)) {
+ if (isProcessorActionComplete(processorEntities, updateRequest, processorIds, desiredState)) {
logger.debug("All {} processors of interest now have the desired state of {}", processorIds.size(), desiredState);
return true;
}
@@ -866,55 +886,93 @@ public class ProcessGroupResource extends ApplicationResource {
return false;
}
- private boolean isProcessorStatusEqual(final ProcessGroupStatusSnapshotDTO statusSnapshot, final Set<String> processorIds, final ScheduledState desiredState) {
+ private boolean isProcessorActionComplete(final Set<ProcessorEntity> processorEntities, final VariableRegistryUpdateRequest updateRequest,
+ final Set<String> processorIds, final ScheduledState desiredState) {
+
final String desiredStateName = desiredState.name();
- final boolean allProcessorsMatch = statusSnapshot.getProcessorStatusSnapshots().stream()
- .map(entity -> entity.getProcessorStatusSnapshot())
- .filter(status -> processorIds.contains(status.getId()))
- .allMatch(status -> {
- final String runStatus = status.getRunStatus();
- final boolean stateMatches = desiredStateName.equalsIgnoreCase(runStatus);
- if (!stateMatches) {
- return false;
- }
+ // update the affected processors
+ processorEntities.stream()
+ .filter(entity -> updateRequest.getAffectedComponents().containsKey(entity.getId()))
+ .forEach(entity -> {
+ final AffectedComponentEntity affectedComponentEntity = updateRequest.getAffectedComponents().get(entity.getId());
+ affectedComponentEntity.setRevision(entity.getRevision());
+
+ // only consider update this component if the user had permissions to it
+ if (Boolean.TRUE.equals(affectedComponentEntity.getPermissions().getCanRead())) {
+ final AffectedComponentDTO affectedComponent = affectedComponentEntity.getComponent();
+ affectedComponent.setState(entity.getStatus().getAggregateSnapshot().getRunStatus());
+ affectedComponent.setActiveThreadCount(entity.getStatus().getAggregateSnapshot().getActiveThreadCount());
+
+ if (Boolean.TRUE.equals(entity.getPermissions().getCanRead())) {
+ affectedComponent.setValidationErrors(entity.getComponent().getValidationErrors());
+ }
+ }
+ });
- if (desiredState == ScheduledState.STOPPED && status.getActiveThreadCount() != 0) {
- return false;
- }
+ final boolean allProcessorsMatch = processorEntities.stream()
+ .filter(entity -> processorIds.contains(entity.getId()))
+ .allMatch(entity -> {
+ final ProcessorStatusDTO status = entity.getStatus();
- return true;
- });
+ final String runStatus = status.getAggregateSnapshot().getRunStatus();
+ final boolean stateMatches = desiredStateName.equalsIgnoreCase(runStatus);
+ if (!stateMatches) {
+ return false;
+ }
+
+ if (desiredState == ScheduledState.STOPPED && status.getAggregateSnapshot().getActiveThreadCount() != 0) {
+ return false;
+ }
+
+ return true;
+ });
if (!allProcessorsMatch) {
return false;
}
- for (final ProcessGroupStatusSnapshotEntity childGroupEntity : statusSnapshot.getProcessGroupStatusSnapshots()) {
- final ProcessGroupStatusSnapshotDTO childGroupStatus = childGroupEntity.getProcessGroupStatusSnapshot();
- final boolean allMatchChildLevel = isProcessorStatusEqual(childGroupStatus, processorIds, desiredState);
- if (!allMatchChildLevel) {
- return false;
- }
- }
-
return true;
}
-
+ /**
+ * Updates the affected controller services in the specified updateRequest with the serviceEntities.
+ *
+ * @param serviceEntities service entities
+ * @param updateRequest update request
+ */
+ private void updateAffectedControllerServices(final Set<ControllerServiceEntity> serviceEntities, final VariableRegistryUpdateRequest updateRequest) {
+ // update the affected components
+ serviceEntities.stream()
+ .filter(entity -> updateRequest.getAffectedComponents().containsKey(entity.getId()))
+ .forEach(entity -> {
+ final AffectedComponentEntity affectedComponentEntity = updateRequest.getAffectedComponents().get(entity.getId());
+ affectedComponentEntity.setRevision(entity.getRevision());
+
+ // only consider update this component if the user had permissions to it
+ if (Boolean.TRUE.equals(affectedComponentEntity.getPermissions().getCanRead())) {
+ final AffectedComponentDTO affectedComponent = affectedComponentEntity.getComponent();
+ affectedComponent.setState(entity.getComponent().getState());
+
+ if (Boolean.TRUE.equals(entity.getPermissions().getCanRead())) {
+ affectedComponent.setValidationErrors(entity.getComponent().getValidationErrors());
+ }
+ }
+ });
+ }
/**
* Periodically polls the process group with the given ID, waiting for all controller services whose ID's are given to have the given Controller Service State.
*
- * @param client the Jersey Client to use for making the HTTP Request
* @param groupId the ID of the Process Group to poll
* @param serviceIds the ID of all Controller Services whose state should be equal to the given desired state
* @param desiredState the desired state for all services with the ID's given
* @param pause the Pause that can be used to wait between polling
* @return <code>true</code> if successful, <code>false</code> if unable to wait for services to reach the desired state
*/
- private boolean waitForControllerServiceStatus(final Client client, final URI originalUri, final String groupId, final Set<String> serviceIds, final ControllerServiceState desiredState,
- final Pause pause) {
+ private boolean waitForControllerServiceStatus(final URI originalUri, final String groupId, final Set<String> serviceIds,
+ final ControllerServiceState desiredState, final VariableRegistryUpdateRequest updateRequest, final Pause pause) throws InterruptedException {
+
URI groupUri;
try {
groupUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
@@ -923,16 +981,31 @@ public class ProcessGroupResource extends ApplicationResource {
throw new RuntimeException(e);
}
+ final Map<String, String> headers = new HashMap<>();
+ final MultivaluedMap<String, String> requestEntity = new MultivaluedMapImpl();
+
boolean continuePolling = true;
while (continuePolling) {
- final ClientResponse response = client.resource(groupUri).header("Content-Type", "application/json").get(ClientResponse.class);
- if (response.getStatus() != Status.OK.getStatusCode()) {
+
+ // Determine whether we should replicate only to the cluster coordinator, or if we should replicate directly to the cluster nodes themselves.
+ final NodeResponse clusterResponse;
+ if (getReplicationTarget() == ReplicationTarget.CLUSTER_NODES) {
+ clusterResponse = getRequestReplicator().replicate(HttpMethod.GET, groupUri, requestEntity, headers).awaitMergedResponse();
+ } else {
+ clusterResponse = getRequestReplicator().forwardToCoordinator(
+ getClusterCoordinatorNode(), HttpMethod.GET, groupUri, requestEntity, headers).awaitMergedResponse();
+ }
+
+ if (clusterResponse.getStatus() != Status.OK.getStatusCode()) {
return false;
}
- final ControllerServicesEntity controllerServicesEntity = response.getEntity(ControllerServicesEntity.class);
+ final ControllerServicesEntity controllerServicesEntity = getResponseEntity(clusterResponse, ControllerServicesEntity.class);
final Set<ControllerServiceEntity> serviceEntities = controllerServicesEntity.getControllerServices();
+ // update the affected controller services
+ updateAffectedControllerServices(serviceEntities, updateRequest);
+
final String desiredStateName = desiredState.name();
final boolean allServicesMatch = serviceEntities.stream()
.map(entity -> entity.getComponent())
@@ -963,11 +1036,16 @@ public class ProcessGroupResource extends ApplicationResource {
* @param user the user that is retrieving the controller services
* @return <code>true</code> if successful, <code>false</code> if unable to wait for services to reach the desired state
*/
- private boolean waitForLocalControllerServiceStatus(final String groupId, final Set<String> serviceIds, final ControllerServiceState desiredState, final Pause pause, final NiFiUser user) {
+ private boolean waitForLocalControllerServiceStatus(final String groupId, final Set<String> serviceIds, final ControllerServiceState desiredState,
+ final VariableRegistryUpdateRequest updateRequest, final Pause pause, final NiFiUser user) {
+
boolean continuePolling = true;
while (continuePolling) {
final Set<ControllerServiceEntity> serviceEntities = serviceFacade.getControllerServices(groupId, false, true, user);
+ // update the affected controller services
+ updateAffectedControllerServices(serviceEntities, updateRequest);
+
final String desiredStateName = desiredState.name();
final boolean allServicesMatch = serviceEntities.stream()
.map(entity -> entity.getComponent())
@@ -975,6 +1053,7 @@ public class ProcessGroupResource extends ApplicationResource {
.map(service -> service.getState())
.allMatch(state -> desiredStateName.equals(state));
+
if (allServicesMatch) {
logger.debug("All {} controller services of interest now have the desired state of {}", serviceIds.size(), desiredState);
return true;
@@ -987,8 +1066,8 @@ public class ProcessGroupResource extends ApplicationResource {
return false;
}
- private VariableRegistryUpdateRequest createVariableRegistryUpdateRequest(final String groupId) {
- final VariableRegistryUpdateRequest updateRequest = new VariableRegistryUpdateRequest(UUID.randomUUID().toString(), groupId);
+ private VariableRegistryUpdateRequest createVariableRegistryUpdateRequest(final String groupId, final Set<AffectedComponentEntity> affectedComponents, final NiFiUser user) {
+ final VariableRegistryUpdateRequest updateRequest = new VariableRegistryUpdateRequest(UUID.randomUUID().toString(), groupId, affectedComponents, user);
// before adding to the request map, purge any old requests. Must do this by creating a List of ID's
// and then removing those ID's one-at-a-time in order to avoid ConcurrentModificationException.
@@ -1011,27 +1090,26 @@ public class ProcessGroupResource extends ApplicationResource {
return updateRequest;
}
- private Response updateVariableRegistryLocal(final String groupId, final List<AffectedComponentDTO> affectedProcessors, final List<AffectedComponentDTO> affectedServices,
- final VariableRegistryEntity requestEntity) {
+ private Response updateVariableRegistryLocal(final String groupId, final Set<AffectedComponentEntity> affectedComponents, final List<AffectedComponentDTO> affectedProcessors,
+ final List<AffectedComponentDTO> affectedServices, final NiFiUser user, final VariableRegistryEntity requestEntity) {
final Set<String> affectedProcessorIds = affectedProcessors == null ? Collections.emptySet() : affectedProcessors.stream()
- .map(component -> component.getComponentId())
+ .map(component -> component.getId())
.collect(Collectors.toSet());
Map<String, Revision> processorRevisionMap = getRevisions(groupId, affectedProcessorIds);
final Set<String> affectedServiceIds = affectedServices == null ? Collections.emptySet() : affectedServices.stream()
- .map(component -> component.getComponentId())
+ .map(component -> component.getId())
.collect(Collectors.toSet());
Map<String, Revision> serviceRevisionMap = getRevisions(groupId, affectedServiceIds);
// update the variable registry
- final VariableRegistryUpdateRequest updateRequest = createVariableRegistryUpdateRequest(groupId);
+ final VariableRegistryUpdateRequest updateRequest = createVariableRegistryUpdateRequest(groupId, affectedComponents, user);
updateRequest.getIdentifyRelevantComponentsStep().setComplete(true);
final Pause pause = createPause(updateRequest);
final Revision requestRevision = getRevision(requestEntity.getProcessGroupRevision(), groupId);
- final NiFiUser user = NiFiUserUtils.getNiFiUser();
final Runnable updateTask = new Runnable() {
@Override
public void run() {
@@ -1052,21 +1130,26 @@ public class ProcessGroupResource extends ApplicationResource {
// Apply the updates
performUpdateVariableRegistryStep(groupId, updateRequest, updateRequest.getApplyUpdatesStep(), "Applying updates to Variable Registry",
- () -> serviceFacade.updateVariableRegistry(user, requestRevision, requestEntity.getVariableRegistry()));
+ () -> {
+ final VariableRegistryEntity entity = serviceFacade.updateVariableRegistry(user, requestRevision, requestEntity.getVariableRegistry());
+ updateRequest.setProcessGroupRevision(entity.getProcessGroupRevision());
+ });
// Re-enable the controller services
performUpdateVariableRegistryStep(groupId, updateRequest, updateRequest.getEnableServicesStep(), "Re-enabling Controller Services",
- () -> enableControllerServices(user, groupId, updatedServiceRevisionMap, pause));
+ () -> enableControllerServices(user, updateRequest, groupId, updatedServiceRevisionMap, pause));
// Restart processors
performUpdateVariableRegistryStep(groupId, updateRequest, updateRequest.getStartProcessorsStep(), "Restarting Processors",
- () -> startProcessors(user, groupId, updatedProcessorRevisionMap, pause));
+ () -> startProcessors(user, updateRequest, groupId, updatedProcessorRevisionMap, pause));
// Set complete
updateRequest.setComplete(true);
updateRequest.setLastUpdated(new Date());
} catch (final Exception e) {
logger.error("Failed to update Variable Registry for Proces Group with ID " + groupId, e);
+
+ updateRequest.setComplete(true);
updateRequest.setFailureReason("An unexpected error has occurred: " + e);
}
}
@@ -1076,10 +1159,11 @@ public class ProcessGroupResource extends ApplicationResource {
variableRegistryThreadPool.submit(updateTask);
final VariableRegistryUpdateRequestEntity responseEntity = new VariableRegistryUpdateRequestEntity();
- responseEntity.setRequestDto(dtoFactory.createVariableRegistryUpdateRequestDto(updateRequest));
- responseEntity.getRequestDto().setUri(generateResourceUri("process-groups", groupId, "variable-registry", "update-requests", updateRequest.getRequestId()));
+ responseEntity.setRequest(dtoFactory.createVariableRegistryUpdateRequestDto(updateRequest));
+ responseEntity.setProcessGroupRevision(updateRequest.getProcessGroupRevision());
+ responseEntity.getRequest().setUri(generateResourceUri("process-groups", groupId, "variable-registry", "update-requests", updateRequest.getRequestId()));
- final URI location = URI.create(responseEntity.getRequestDto().getUri());
+ final URI location = URI.create(responseEntity.getRequest().getUri());
return Response.status(Status.ACCEPTED).location(location).entity(responseEntity).build();
}
@@ -1103,11 +1187,12 @@ public class ProcessGroupResource extends ApplicationResource {
action.run();
step.setComplete(true);
} catch (final Exception e) {
- request.setComplete(true);
logger.error("Failed to update variable registry for Process Group with ID {}", groupId, e);
step.setComplete(true);
step.setFailureReason(e.getMessage());
+
+ request.setComplete(true);
request.setFailureReason("Failed to update Variable Registry because failed while performing step: " + stepDescription);
}
@@ -1123,21 +1208,21 @@ public class ProcessGroupResource extends ApplicationResource {
serviceFacade.verifyScheduleComponents(processGroupId, ScheduledState.STOPPED, processorRevisions.keySet());
serviceFacade.scheduleComponents(user, processGroupId, ScheduledState.STOPPED, processorRevisions);
- waitForLocalProcessorStatus(processGroupId, processorRevisions.keySet(), ScheduledState.STOPPED, pause);
+ waitForLocalProcessor(processGroupId, processorRevisions.keySet(), ScheduledState.STOPPED, updateRequest, pause);
}
- private void startProcessors(final NiFiUser user, final String processGroupId, final Map<String, Revision> processorRevisions, final Pause pause) {
+ private void startProcessors(final NiFiUser user, final VariableRegistryUpdateRequest request, final String processGroupId, final Map<String, Revision> processorRevisions, final Pause pause) {
if (processorRevisions.isEmpty()) {
return;
}
serviceFacade.verifyScheduleComponents(processGroupId, ScheduledState.RUNNING, processorRevisions.keySet());
serviceFacade.scheduleComponents(user, processGroupId, ScheduledState.RUNNING, processorRevisions);
- waitForLocalProcessorStatus(processGroupId, processorRevisions.keySet(), ScheduledState.RUNNING, pause);
+ waitForLocalProcessor(processGroupId, processorRevisions.keySet(), ScheduledState.RUNNING, request, pause);
}
private void disableControllerServices(final NiFiUser user, final VariableRegistryUpdateRequest updateRequest, final String processGroupId,
- final Map<String, Revision> serviceRevisions, final Pause pause) {
+ final Map<String, Revision> serviceRevisions, final Pause pause) {
if (serviceRevisions.isEmpty()) {
return;
@@ -1145,116 +1230,141 @@ public class ProcessGroupResource extends ApplicationResource {
serviceFacade.verifyActivateControllerServices(processGroupId, ControllerServiceState.DISABLED, serviceRevisions.keySet());
serviceFacade.activateControllerServices(user, processGroupId, ControllerServiceState.DISABLED, serviceRevisions);
- waitForLocalControllerServiceStatus(processGroupId, serviceRevisions.keySet(), ControllerServiceState.DISABLED, pause, user);
+ waitForLocalControllerServiceStatus(processGroupId, serviceRevisions.keySet(), ControllerServiceState.DISABLED, updateRequest, pause, user);
}
- private void enableControllerServices(final NiFiUser user, final String processGroupId, final Map<String, Revision> serviceRevisions, final Pause pause) {
+ private void enableControllerServices(final NiFiUser user, final VariableRegistryUpdateRequest updateRequest, final String processGroupId,
+ final Map<String, Revision> serviceRevisions, final Pause pause) {
+
if (serviceRevisions.isEmpty()) {
return;
}
serviceFacade.verifyActivateControllerServices(processGroupId, ControllerServiceState.ENABLED, serviceRevisions.keySet());
serviceFacade.activateControllerServices(user, processGroupId, ControllerServiceState.ENABLED, serviceRevisions);
- waitForLocalControllerServiceStatus(processGroupId, serviceRevisions.keySet(), ControllerServiceState.ENABLED, pause, user);
+ waitForLocalControllerServiceStatus(processGroupId, serviceRevisions.keySet(), ControllerServiceState.ENABLED, updateRequest, pause, user);
}
- private void scheduleProcessors(final String groupId, final URI originalUri, final Client jerseyClient, final VariableRegistryUpdateRequest updateRequest,
- final Pause pause, final Collection<AffectedComponentDTO> affectedProcessors, final ScheduledState desiredState, final VariableRegistryUpdateStep updateStep) {
+ private void scheduleProcessors(final String groupId, final URI originalUri, final VariableRegistryUpdateRequest updateRequest,
+ final Pause pause, final Collection<AffectedComponentDTO> affectedProcessors, final ScheduledState desiredState,
+ final VariableRegistryUpdateStep updateStep) throws InterruptedException {
+
final Set<String> affectedProcessorIds = affectedProcessors.stream()
- .map(component -> component.getComponentId())
+ .map(component -> component.getId())
.collect(Collectors.toSet());
final Map<String, Revision> processorRevisionMap = getRevisions(groupId, affectedProcessorIds);
final Map<String, RevisionDTO> processorRevisionDtoMap = processorRevisionMap.entrySet().stream().collect(
Collectors.toMap(Map.Entry::getKey, entry -> dtoFactory.createRevisionDTO(entry.getValue())));
- final ScheduleComponentsEntity stopProcessorsEntity = new ScheduleComponentsEntity();
- stopProcessorsEntity.setComponents(processorRevisionDtoMap);
- stopProcessorsEntity.setId(groupId);
- stopProcessorsEntity.setState(desiredState.name());
+ final ScheduleComponentsEntity scheduleProcessorsEntity = new ScheduleComponentsEntity();
+ scheduleProcessorsEntity.setComponents(processorRevisionDtoMap);
+ scheduleProcessorsEntity.setId(groupId);
+ scheduleProcessorsEntity.setState(desiredState.name());
- URI stopProcessorUri;
+ URI scheduleGroupUri;
try {
- stopProcessorUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
+ scheduleGroupUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
originalUri.getPort(), "/nifi-api/flow/process-groups/" + groupId, null, originalUri.getFragment());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
- final ClientResponse stopProcessorResponse = jerseyClient.resource(stopProcessorUri)
- .header("Content-Type", "application/json")
- .entity(stopProcessorsEntity)
- .put(ClientResponse.class);
+ final Map<String, String> headers = new HashMap<>();
+ headers.put("content-type", MediaType.APPLICATION_JSON);
+
+ // Determine whether we should replicate only to the cluster coordinator, or if we should replicate directly to the cluster nodes themselves.
+ final NodeResponse clusterResponse;
+ if (getReplicationTarget() == ReplicationTarget.CLUSTER_NODES) {
+ clusterResponse = getRequestReplicator().replicate(HttpMethod.PUT, scheduleGroupUri, scheduleProcessorsEntity, headers).awaitMergedResponse();
+ } else {
+ clusterResponse = getRequestReplicator().forwardToCoordinator(
+ getClusterCoordinatorNode(), HttpMethod.PUT, scheduleGroupUri, scheduleProcessorsEntity, headers).awaitMergedResponse();
+ }
- final int stopProcessorStatus = stopProcessorResponse.getStatus();
+ final int stopProcessorStatus = clusterResponse.getStatus();
if (stopProcessorStatus != Status.OK.getStatusCode()) {
updateRequest.getStopProcessorsStep().setFailureReason("Failed while " + updateStep.getDescription());
+
+ updateStep.setComplete(true);
updateRequest.setFailureReason("Failed while " + updateStep.getDescription());
return;
}
updateRequest.setLastUpdated(new Date());
- final boolean processorsTransitioned = waitForProcessorStatus(jerseyClient, originalUri, groupId, affectedProcessorIds, desiredState, pause);
- if (processorsTransitioned) {
- updateStep.setComplete(true);
- } else {
+ final boolean processorsTransitioned = waitForProcessorStatus(originalUri, groupId, affectedProcessorIds, desiredState, updateRequest, pause);
+ updateStep.setComplete(true);
+
+ if (!processorsTransitioned) {
updateStep.setFailureReason("Failed while " + updateStep.getDescription());
+
+ updateRequest.setComplete(true);
updateRequest.setFailureReason("Failed while " + updateStep.getDescription());
- return;
}
}
- private void activateControllerServices(final String groupId, final URI originalUri, final Client jerseyClient, final VariableRegistryUpdateRequest updateRequest,
- final Pause pause, final Collection<AffectedComponentDTO> affectedServices, final ControllerServiceState desiredState, final VariableRegistryUpdateStep updateStep) {
+ private void activateControllerServices(final String groupId, final URI originalUri, final VariableRegistryUpdateRequest updateRequest,
+ final Pause pause, final Collection<AffectedComponentDTO> affectedServices, final ControllerServiceState desiredState, final VariableRegistryUpdateStep updateStep) throws InterruptedException {
final Set<String> affectedServiceIds = affectedServices.stream()
- .map(component -> component.getComponentId())
+ .map(component -> component.getId())
.collect(Collectors.toSet());
final Map<String, Revision> serviceRevisionMap = getRevisions(groupId, affectedServiceIds);
final Map<String, RevisionDTO> serviceRevisionDtoMap = serviceRevisionMap.entrySet().stream().collect(
Collectors.toMap(Map.Entry::getKey, entry -> dtoFactory.createRevisionDTO(entry.getValue())));
- final ActivateControllerServicesEntity disableServicesEntity = new ActivateControllerServicesEntity();
- disableServicesEntity.setComponents(serviceRevisionDtoMap);
- disableServicesEntity.setId(groupId);
- disableServicesEntity.setState(desiredState.name());
+ final ActivateControllerServicesEntity activateServicesEntity = new ActivateControllerServicesEntity();
+ activateServicesEntity.setComponents(serviceRevisionDtoMap);
+ activateServicesEntity.setId(groupId);
+ activateServicesEntity.setState(desiredState.name());
- URI disableServicesUri;
+ URI controllerServicesUri;
try {
- disableServicesUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
+ controllerServicesUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
originalUri.getPort(), "/nifi-api/flow/process-groups/" + groupId + "/controller-services", null, originalUri.getFragment());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
- final ClientResponse disableServicesResponse = jerseyClient.resource(disableServicesUri)
- .header("Content-Type", "application/json")
- .entity(disableServicesEntity)
- .put(ClientResponse.class);
+ final Map<String, String> headers = new HashMap<>();
+ headers.put("content-type", MediaType.APPLICATION_JSON);
- final int disableServicesStatus = disableServicesResponse.getStatus();
+ // Determine whether we should replicate only to the cluster coordinator, or if we should replicate directly to the cluster nodes themselves.
+ final NodeResponse clusterResponse;
+ if (getReplicationTarget() == ReplicationTarget.CLUSTER_NODES) {
+ clusterResponse = getRequestReplicator().replicate(HttpMethod.PUT, controllerServicesUri, activateServicesEntity, headers).awaitMergedResponse();
+ } else {
+ clusterResponse = getRequestReplicator().forwardToCoordinator(
+ getClusterCoordinatorNode(), HttpMethod.PUT, controllerServicesUri, activateServicesEntity, headers).awaitMergedResponse();
+ }
+
+ final int disableServicesStatus = clusterResponse.getStatus();
if (disableServicesStatus != Status.OK.getStatusCode()) {
updateStep.setFailureReason("Failed while " + updateStep.getDescription());
+
+ updateStep.setComplete(true);
updateRequest.setFailureReason("Failed while " + updateStep.getDescription());
return;
}
updateRequest.setLastUpdated(new Date());
- if (waitForControllerServiceStatus(jerseyClient, originalUri, groupId, affectedServiceIds, desiredState, pause)) {
- updateStep.setComplete(true);
- } else {
+ final boolean serviceTransitioned = waitForControllerServiceStatus(originalUri, groupId, affectedServiceIds, desiredState, updateRequest, pause);
+ updateStep.setComplete(true);
+
+ if (!serviceTransitioned) {
updateStep.setFailureReason("Failed while " + updateStep.getDescription());
+
+ updateRequest.setComplete(true);
updateRequest.setFailureReason("Failed while " + updateStep.getDescription());
- return;
}
}
+ private void applyVariableRegistryUpdate(final String groupId, final URI originalUri, final VariableRegistryUpdateRequest updateRequest,
+ final VariableRegistryEntity updateEntity) throws InterruptedException, IOException {
- private void applyVariableRegistryUpdate(final String groupId, final URI originalUri, final Client jerseyClient, final VariableRegistryUpdateRequest updateRequest,
- final VariableRegistryEntity updateEntity) {
-
+ // convert request accordingly
URI applyUpdatesUri;
try {
applyUpdatesUri = new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
@@ -1263,21 +1373,53 @@ public class ProcessGroupResource extends ApplicationResource {
throw new RuntimeException(e);
}
- final ClientResponse applyUpdatesResponse = jerseyClient.resource(applyUpdatesUri)
- .header("Content-Type", "application/json")
- .entity(updateEntity)
- .put(ClientResponse.class);
+ final Map<String, String> headers = new HashMap<>();
+ headers.put("content-type", MediaType.APPLICATION_JSON);
- final int applyUpdatesStatus = applyUpdatesResponse.getStatus();
+ // Determine whether we should replicate only to the cluster coordinator, or if we should replicate directly to the cluster nodes themselves.
+ final NodeResponse clusterResponse;
+ if (getReplicationTarget() == ReplicationTarget.CLUSTER_NODES) {
+ clusterResponse = getRequestReplicator().replicate(HttpMethod.PUT, applyUpdatesUri, updateEntity, headers).awaitMergedResponse();
+ } else {
+ clusterResponse = getRequestReplicator().forwardToCoordinator(
+ getClusterCoordinatorNode(), HttpMethod.PUT, applyUpdatesUri, updateEntity, headers).awaitMergedResponse();
+ }
+
+ final int applyUpdatesStatus = clusterResponse.getStatus();
updateRequest.setLastUpdated(new Date());
- if (applyUpdatesStatus != Status.OK.getStatusCode()) {
- updateRequest.getApplyUpdatesStep().setFailureReason("Failed to apply updates to the Variable Registry");
- updateRequest.setFailureReason("Failed to apply updates to the Variable Registry");
- return;
+ updateRequest.getApplyUpdatesStep().setComplete(true);
+
+ if (applyUpdatesStatus == Status.OK.getStatusCode()) {
+ // grab the current process group revision
+ final VariableRegistryEntity entity = getResponseEntity(clusterResponse, VariableRegistryEntity.class);
+ updateRequest.setProcessGroupRevision(entity.getProcessGroupRevision());
+ } else {
+ final String message = getResponseEntity(clusterResponse, String.class);
+
+ // update the request progress
+ updateRequest.getApplyUpdatesStep().setFailureReason("Failed to apply updates to the Variable Registry: " + message);
+ updateRequest.setComplete(true);
+ updateRequest.setFailureReason("Failed to apply updates to the Variable Registry: " + message);
}
}
/**
+ * Extracts the response entity from the specified node response.
+ *
+ * @param nodeResponse node response
+ * @param clazz class
+ * @param <T> type of class
+ * @return the response entity
+ */
+ private <T> T getResponseEntity(final NodeResponse nodeResponse, final Class<T> clazz) {
+ T entity = (T) nodeResponse.getUpdatedEntity();
+ if (entity == null) {
+ entity = nodeResponse.getClientResponse().getEntity(clazz);
+ }
+ return entity;
+ }
+
+ /**
* Removes the specified process group reference.
*
* @param httpServletRequest request
@@ -1676,7 +1818,8 @@ public class ProcessGroupResource extends ApplicationResource {
value = "The process group id.",
required = true
)
- @PathParam("id") final String groupId) {
+ @PathParam("id") final String groupId,
+ @ApiParam("Whether or not to include processors from descendant process groups") @QueryParam("includeDescendantGroups") @DefaultValue("false") boolean includeDescendantGroups) {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
@@ -1689,7 +1832,7 @@ public class ProcessGroupResource extends ApplicationResource {
});
// get the processors
- final Set<ProcessorEntity> processors = serviceFacade.getProcessors(groupId);
+ final Set<ProcessorEntity> processors = serviceFacade.getProcessors(groupId, includeDescendantGroups);
// create the response entity
final ProcessorsEntity entity = new ProcessorsEntity();
@@ -3121,7 +3264,6 @@ public class ProcessGroupResource extends ApplicationResource {
uriBuilder.segment("process-groups", groupId, "templates", "import");
final URI importUri = uriBuilder.build();
- // change content type to XML for serializing entity
final Map<String, String> headersToOverride = new HashMap<>();
headersToOverride.put("content-type", MediaType.APPLICATION_XML);
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
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/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index ed42e9f..1a4d52b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -16,33 +16,6 @@
*/
package org.apache.nifi.web.api.dto;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import javax.ws.rs.WebApplicationException;
-
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.action.Action;
@@ -182,6 +155,7 @@ import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusSnapshotDTO;
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import org.apache.nifi.web.api.entity.AllowableValueEntity;
import org.apache.nifi.web.api.entity.BulletinEntity;
import org.apache.nifi.web.api.entity.ComponentReferenceEntity;
@@ -196,6 +170,32 @@ import org.apache.nifi.web.api.entity.VariableEntity;
import org.apache.nifi.web.controller.ControllerFacade;
import org.apache.nifi.web.revision.RevisionManager;
+import javax.ws.rs.WebApplicationException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
public final class DtoFactory {
@SuppressWarnings("rawtypes")
@@ -1737,13 +1737,29 @@ public final class DtoFactory {
public AffectedComponentDTO createAffectedComponentDto(final ConfiguredComponent component) {
final AffectedComponentDTO dto = new AffectedComponentDTO();
- dto.setComponentId(component.getIdentifier());
- dto.setParentGroupId(component.getProcessGroupIdentifier());
+ dto.setId(component.getIdentifier());
+ dto.setName(component.getName());
+ dto.setProcessGroupId(component.getProcessGroupIdentifier());
if (component instanceof ProcessorNode) {
- dto.setComponentType(AffectedComponentDTO.COMPONENT_TYPE_PROCESSOR);
+ final ProcessorNode node = ((ProcessorNode) component);
+ dto.setState(node.getScheduledState().name());
+ dto.setActiveThreadCount(node.getActiveThreadCount());
+ dto.setReferenceType(AffectedComponentDTO.COMPONENT_TYPE_PROCESSOR);
} else if (component instanceof ControllerServiceNode) {
- dto.setComponentType(AffectedComponentDTO.COMPONENT_TYPE_CONTROLLER_SERVICE);
+ final ControllerServiceNode node = ((ControllerServiceNode) component);
+ dto.setState(node.getState().name());
+ dto.setReferenceType(AffectedComponentDTO.COMPONENT_TYPE_CONTROLLER_SERVICE);
+ }
+
+ final Collection<ValidationResult> validationErrors = component.getValidationErrors();
+ if (validationErrors != null && !validationErrors.isEmpty()) {
+ final List<String> errors = new ArrayList<>();
+ for (final ValidationResult validationResult : validationErrors) {
+ errors.add(validationResult.toString());
+ }
+
+ dto.setValidationErrors(errors);
}
return dto;
@@ -2114,8 +2130,18 @@ public final class DtoFactory {
return deprecationNotice == null ? null : deprecationNotice.reason();
}
+ public Set<AffectedComponentEntity> createAffectedComponentEntities(final Set<ConfiguredComponent> affectedComponents, final RevisionManager revisionManager) {
+ return affectedComponents.stream()
+ .map(component -> {
+ final AffectedComponentDTO affectedComponent = createAffectedComponentDto(component);
+ final PermissionsDTO permissions = createPermissionsDto(component);
+ final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(component.getIdentifier()));
+ return entityFactory.createAffectedComponentEntity(affectedComponent, revision, permissions);
+ })
+ .collect(Collectors.toSet());
+ }
- public VariableRegistryDTO createVariableRegistryDto(final ProcessGroup processGroup) {
+ public VariableRegistryDTO createVariableRegistryDto(final ProcessGroup processGroup, final RevisionManager revisionManager) {
final ComponentVariableRegistry variableRegistry = processGroup.getVariableRegistry();
final List<String> variableNames = variableRegistry.getVariableMap().keySet().stream()
@@ -2130,21 +2156,18 @@ public final class DtoFactory {
variableDto.setValue(variableRegistry.getVariableValue(variableName));
variableDto.setProcessGroupId(processGroup.getIdentifier());
- final Set<ConfiguredComponent> affectedComponents = processGroup.getComponentsAffectedByVariable(variableName);
- final Set<AffectedComponentDTO> affectedComponentDtos = affectedComponents.stream()
- .map(component -> createAffectedComponentDto(component))
- .collect(Collectors.toSet());
+ final Set<AffectedComponentEntity> affectedComponentEntities = createAffectedComponentEntities(processGroup.getComponentsAffectedByVariable(variableName), revisionManager);
boolean canWrite = true;
- for (final ConfiguredComponent component : affectedComponents) {
- final PermissionsDTO permissions = createPermissionsDto(component);
+ for (final AffectedComponentEntity affectedComponent : affectedComponentEntities) {
+ final PermissionsDTO permissions = affectedComponent.getPermissions();
if (!permissions.getCanRead() || !permissions.getCanWrite()) {
canWrite = false;
break;
}
}
- variableDto.setAffectedComponents(affectedComponentDtos);
+ variableDto.setAffectedComponents(affectedComponentEntities);
final VariableEntity variableEntity = new VariableEntity();
variableEntity.setVariable(variableDto);
@@ -2178,6 +2201,8 @@ public final class DtoFactory {
updateSteps.add(createVariableRegistryUpdateStepDto(request.getStartProcessorsStep()));
dto.setUpdateSteps(updateSteps);
+ dto.setAffectedComponents(new HashSet<>(request.getAffectedComponents().values()));
+
return dto;
}
@@ -2190,42 +2215,41 @@ public final class DtoFactory {
}
- public VariableRegistryDTO populateAffectedComponents(final VariableRegistryDTO variableRegistry, final ProcessGroup group) {
+ public VariableRegistryDTO populateAffectedComponents(final VariableRegistryDTO variableRegistry, final ProcessGroup group, final RevisionManager revisionManager) {
if (!group.getIdentifier().equals(variableRegistry.getProcessGroupId())) {
throw new IllegalArgumentException("Variable Registry does not have the same Group ID as the given Process Group");
}
final Set<VariableEntity> variableEntities = new LinkedHashSet<>();
- for (final VariableEntity inputEntity : variableRegistry.getVariables()) {
- final VariableEntity entity = new VariableEntity();
+ if (variableRegistry.getVariables() != null) {
+ for (final VariableEntity inputEntity : variableRegistry.getVariables()) {
+ final VariableEntity entity = new VariableEntity();
- final VariableDTO inputDto = inputEntity.getVariable();
- final VariableDTO variableDto = new VariableDTO();
- variableDto.setName(inputDto.getName());
- variableDto.setValue(inputDto.getValue());
- variableDto.setProcessGroupId(group.getIdentifier());
+ final VariableDTO inputDto = inputEntity.getVariable();
+ final VariableDTO variableDto = new VariableDTO();
+ variableDto.setName(inputDto.getName());
+ variableDto.setValue(inputDto.getValue());
+ variableDto.setProcessGroupId(group.getIdentifier());
- final Set<ConfiguredComponent> affectedComponents = group.getComponentsAffectedByVariable(variableDto.getName());
- final Set<AffectedComponentDTO> affectedComponentDtos = affectedComponents.stream()
- .map(component -> createAffectedComponentDto(component))
- .collect(Collectors.toSet());
+ final Set<AffectedComponentEntity> affectedComponentEntities = createAffectedComponentEntities(group.getComponentsAffectedByVariable(variableDto.getName()), revisionManager);
- boolean canWrite = true;
- for (final ConfiguredComponent component : affectedComponents) {
- final PermissionsDTO permissions = createPermissionsDto(component);
- if (!permissions.getCanRead() || !permissions.getCanWrite()) {
- canWrite = false;
- break;
+ boolean canWrite = true;
+ for (final AffectedComponentEntity affectedComponent : affectedComponentEntities) {
+ final PermissionsDTO permissions = affectedComponent.getPermissions();
+ if (!permissions.getCanRead() || !permissions.getCanWrite()) {
+ canWrite = false;
+ break;
+ }
}
- }
- variableDto.setAffectedComponents(affectedComponentDtos);
+ variableDto.setAffectedComponents(affectedComponentEntities);
- entity.setCanWrite(canWrite);
- entity.setVariable(inputDto);
+ entity.setCanWrite(canWrite);
+ entity.setVariable(inputDto);
- variableEntities.add(entity);
+ variableEntities.add(entity);
+ }
}
final VariableRegistryDTO registryDto = new VariableRegistryDTO();
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
----------------------------------------------------------------------
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 a7f370a..16781c6 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
@@ -33,6 +33,7 @@ import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
import org.apache.nifi.web.api.entity.ActionEntity;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import org.apache.nifi.web.api.entity.AllowableValueEntity;
import org.apache.nifi.web.api.entity.BulletinEntity;
import org.apache.nifi.web.api.entity.ComponentReferenceEntity;
@@ -311,6 +312,20 @@ public final class EntityFactory {
return entity;
}
+ public AffectedComponentEntity createAffectedComponentEntity(final AffectedComponentDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) {
+ final AffectedComponentEntity entity = new AffectedComponentEntity();
+ entity.setRevision(revision);
+ if (dto != null) {
+ entity.setPermissions(permissions);
+ entity.setId(dto.getId());
+
+ if (permissions != null && permissions.getCanRead()) {
+ entity.setComponent(dto);
+ }
+ }
+ return entity;
+ }
+
public UserGroupEntity createUserGroupEntity(final UserGroupDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) {
final UserGroupEntity entity = new UserGroupEntity();
entity.setRevision(revision);
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessorDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessorDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessorDAO.java
index 1d88161..a1bf170 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessorDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessorDAO.java
@@ -59,9 +59,10 @@ public interface ProcessorDAO {
* Gets all the Processor transfer objects for this controller.
*
* @param groupId group id
+ * @param includeDescendants if processors from descendant groups should be included
* @return List of all the Processors
*/
- Set<ProcessorNode> getProcessors(String groupId);
+ Set<ProcessorNode> getProcessors(String groupId, boolean includeDescendants);
/**
* Verifies the specified processor can be updated.
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
----------------------------------------------------------------------
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/StandardProcessorDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
index e11f9ad..429592c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
@@ -58,6 +58,7 @@ import java.util.Set;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
+import java.util.stream.Collectors;
public class StandardProcessorDAO extends ComponentDAO implements ProcessorDAO {
@@ -307,9 +308,13 @@ public class StandardProcessorDAO extends ComponentDAO implements ProcessorDAO {
}
@Override
- public Set<ProcessorNode> getProcessors(String groupId) {
+ public Set<ProcessorNode> getProcessors(String groupId, boolean includeDescendants) {
ProcessGroup group = locateProcessGroup(flowController, groupId);
- return group.getProcessors();
+ if (includeDescendants) {
+ return group.findAllProcessors().stream().collect(Collectors.toSet());
+ } else {
+ return group.getProcessors();
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 0d60df2..72cd5ed 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -461,6 +461,7 @@
<include>${staging.dir}/js/nf/canvas/nf-port-configuration.js</include>
<include>${staging.dir}/js/nf/canvas/nf-port-details.js</include>
<include>${staging.dir}/js/nf/canvas/nf-process-group-configuration.js</include>
+ <include>${staging.dir}/js/nf/canvas/nf-variable-registry.js</include>
<include>${staging.dir}/js/nf/canvas/nf-component-version.js</include>
<include>${staging.dir}/js/nf/canvas/nf-remote-process-group-configuration.js</include>
<include>${staging.dir}/js/nf/canvas/nf-remote-process-group-details.js</include>
[2/4] nifi git commit: NIFI-4280: - Adding support for the user to
configure variables in the UI. - Updating the endpoints for changing
variables as necessary. This closes #2135.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index a3ab7fc..9b04ece 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -43,6 +43,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-ng-bridge.js?
<script type="text/javascript" src="js/nf/canvas/nf-port-configuration.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-port-details.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-process-group-configuration.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/nf-variable-registry.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-component-version.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-remote-process-group-configuration.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-remote-process-group-details.js?${project.version}"></script>\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index 1928f7a..3c7d407 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -129,6 +129,7 @@
<jsp:include page="/WEB-INF/partials/canvas/reporting-task-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/processor-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/processor-details.jsp"/>
+ <jsp:include page="/WEB-INF/partials/canvas/variable-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/process-group-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/override-policy-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/policy-management.jsp"/>
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/variable-configuration.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/variable-configuration.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/variable-configuration.jsp
new file mode 100644
index 0000000..f26c2eb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/variable-configuration.jsp
@@ -0,0 +1,93 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="variable-registry-dialog" class="hidden">
+ <div class="dialog-content">
+ <div class="settings-left">
+ <div class="setting">
+ <div style="float: left;">
+ <div class="setting-name">Process Group</div>
+ <div class="setting-field">
+ <span id="process-group-variable-registry"></span>
+ <span id="variable-registry-process-group-id" class="hidden"></span>
+ </div>
+ </div>
+ <div id="add-variable"><button class="button fa fa-plus"></button></div>
+ <div class="clear"></div>
+ </div>
+ <div id="variable-registry-table"></div>
+ <div id="variable-update-status" class="hidden">
+ <div class="setting">
+ <div class="setting-name">
+ Steps to update variables
+ </div>
+ <div class="setting-field">
+ <ol id="variable-update-steps"></ol>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="spacer"> </div>
+ <div class="settings-right">
+ <div class="setting">
+ <div class="setting-name">
+ Variables
+ </div>
+ <div class="setting-field">
+ <div id="affected-components-context"></div>
+ </div>
+ </div>
+ <div class="setting">
+ <div class="setting-name">
+ Referencing Processors
+ <div class="fa fa-question-circle" alt="Info" title="Processors referencing this variable."></div>
+ </div>
+ <div class="setting-field">
+ <ul id="variable-registry-affected-processors"></ul>
+ </div>
+ </div>
+ <div class="setting">
+ <div class="setting-name">
+ Referencing Controller Services
+ <div class="fa fa-question-circle" alt="Info" title="Controller Services referencing this variable."></div>
+ </div>
+ <div class="setting-field">
+ <ul id="variable-registry-affected-controller-services"></ul>
+ </div>
+ </div>
+ <div class="setting">
+ <div class="setting-name">
+ Unauthorized referencing components
+ <div class="fa fa-question-circle" alt="Info" title="Referencing components for which READ or WRITE permissions are not granted."></div>
+ </div>
+ <div class="setting-field">
+ <ul id="variable-registry-affected-unauthorized-components"></ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div id="new-variable-dialog" class="dialog cancellable small-dialog hidden">
+ <div class="dialog-content">
+ <div>
+ <div class="setting-name">Variable name</div>
+ <div class="setting-field new-variable-name-container">
+ <input id="new-variable-name" type="text"/>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
index 49b7376..fe5808e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
@@ -79,6 +79,10 @@ ul.referencing-component-listing li {
white-space: nowrap;
}
+div.referencing-component-state {
+ width: 13px;
+}
+
div.referencing-component-state.disabled:before {
content: '\e802';
font-family: flowfont;
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
----------------------------------------------------------------------
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 1e01919..cb5282b 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
@@ -213,6 +213,45 @@ div.progress-label {
}
/*
+ Variable Registry
+ */
+
+#variable-registry-dialog {
+ width: 850px;
+ height: 575px;
+}
+
+#variable-registry-dialog div.settings-left {
+ float: left;
+ width: 65%;
+}
+
+#variable-registry-dialog div.settings-right {
+ float: left;
+ width: 33%;
+}
+
+#variable-registry-table {
+ height: 400px;
+}
+
+#add-variable {
+ float: right;
+ margin-bottom: 4px;
+ font-size: 16px;
+ text-transform: uppercase;
+}
+
+li.affected-component-container {
+ margin-bottom: 3px;
+ height: 16px;
+}
+
+div.slick-cell div.overridden {
+ text-decoration: line-through;
+}
+
+/*
General dialog styles.
*/
@@ -239,3 +278,15 @@ ul.result li {
float: left;
width: 2%;
}
+
+div.variable-step {
+ width: 16px;
+ height: 16px;
+ background-color: transparent;
+ float: right;
+}
+
+#variable-update-steps li {
+ width: 300px;
+ margin-bottom: 2px;
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index d1006ab..abd27b2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -1682,14 +1682,14 @@
// build the new property dialog
var newPropertyDialogMarkup =
'<div id="new-property-dialog" class="dialog cancellable small-dialog hidden">' +
- '<div class="dialog-content">' +
- '<div>' +
- '<div class="setting-name">Property name</div>' +
- '<div class="setting-field new-property-name-container">' +
- '<input class="new-property-name" type="text"/>' +
- '</div>' +
- '</div>' +
- '</div>' +
+ '<div class="dialog-content">' +
+ '<div>' +
+ '<div class="setting-name">Property name</div>' +
+ '<div class="setting-field new-property-name-container">' +
+ '<input class="new-property-name" type="text"/>' +
+ '</div>' +
+ '</div>' +
+ '</div>' +
'</div>';
var newPropertyDialog = $(newPropertyDialogMarkup).appendTo(options.dialogContainer);
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
index 7050df0..90a1140 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
@@ -31,6 +31,7 @@
'nf.GoTo',
'nf.ng.Bridge',
'nf.Shell',
+ 'nf.VariableRegistry',
'nf.ComponentState',
'nf.Draggable',
'nf.Birdseye',
@@ -54,8 +55,8 @@
'nf.ComponentVersion',
'nf.QueueListing',
'nf.StatusHistory'],
- function ($, d3, nfCanvasUtils, nfCommon, nfDialog, nfClient, nfErrorHandler, nfClipboard, nfSnippet, nfGoto, nfNgBridge, nfShell, nfComponentState, nfDraggable, nfBirdseye, nfConnection, nfGraph, nfProcessGroupConfiguration, nfProcessorConfiguration, nfProcessorDetails, nfLabelConfiguration, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupDetails, nfPortConfiguration, nfPortDetails, nfConnectionConfiguration, nfConnectionDetails, nfPolicyManagement, nfRemoteProcessGroup, nfLabel, nfProcessor, nfRemoteProcessGroupPorts, nfComponentVersion, nfQueueListing, nfStatusHistory) {
- return (nf.Actions = factory($, d3, nfCanvasUtils, nfCommon, nfDialog, nfClient, nfErrorHandler, nfClipboard, nfSnippet, nfGoto, nfNgBridge, nfShell, nfComponentState, nfDraggable, nfBirdseye, nfConnection, nfGraph, nfProcessGroupConfiguration, nfProcessorConfiguration, nfProcessorDetails, nfLabelConfiguration, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupDetails, nfPortConfiguration, nfPortDetails, nfConnectionConfiguration, nfConnectionDetails, nfPolicyManagement, nfRemoteProcessGroup, nfLabel, nfProcessor, nfRemoteProcessGroupPorts, nfComponentVersion, nfQueueListing, nfStatusHistory));
+ function ($, d3, nfCanvasUtils, nfCommon, nfDialog, nfClient, nfErrorHandler, nfClipboard, nfSnippet, nfGoto, nfNgBridge, nfShell, nfVariableRegistry, nfComponentState, nfDraggable, nfBirdseye, nfConnection, nfGraph, nfProcessGroupConfiguration, nfProcessorConfiguration, nfProcessorDetails, nfLabelConfiguration, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupDetails, nfPortConfiguration, nfPortDetails, nfConnectionConfiguration, nfConnectionDetails, nfPolicyManagement, nfRemoteProcessGroup, nfLabel, nfProcessor, nfRemoteProcessGroupPorts, nfComponentVersion, nfQueueListing, nfStatusHistory) {
+ return (nf.Actions = factory($, d3, nfCanvasUtils, nfCommon, nfDialog, nfClient, nfErrorHandler, nfClipboard, nfSnippet, nfGoto, nfNgBridge, nfShell, nfVariableRegistry, nfComponentState, nfDraggable, nfBirdseye, nfConnection, nfGraph, nfProcessGroupConfiguration, nfProcessorConfiguration, nfProcessorDetails, nfLabelConfiguration, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupDetails, nfPortConfiguration, nfPortDetails, nfConnectionConfiguration, nfConnectionDetails, nfPolicyManagement, nfRemoteProcessGroup, nfLabel, nfProcessor, nfRemoteProcessGroupPorts, nfComponentVersion, nfQueueListing, nfStatusHistory));
});
} else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.Actions =
@@ -71,6 +72,7 @@
require('nf.GoTo'),
require('nf.ng.Bridge'),
require('nf.Shell'),
+ require('nf.VariableRegistry'),
require('nf.ComponentState'),
require('nf.Draggable'),
require('nf.Birdseye'),
@@ -107,6 +109,7 @@
root.nf.GoTo,
root.nf.ng.Bridge,
root.nf.Shell,
+ root.nf.VariableRegistry,
root.nf.ComponentState,
root.nf.Draggable,
root.nf.Birdseye,
@@ -131,7 +134,7 @@
root.nf.QueueListing,
root.nf.StatusHistory);
}
-}(this, function ($, d3, nfCanvasUtils, nfCommon, nfDialog, nfClient, nfErrorHandler, nfClipboard, nfSnippet, nfGoto, nfNgBridge, nfShell, nfComponentState, nfDraggable, nfBirdseye, nfConnection, nfGraph, nfProcessGroupConfiguration, nfProcessorConfiguration, nfProcessorDetails, nfLabelConfiguration, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupDetails, nfPortConfiguration, nfPortDetails, nfConnectionConfiguration, nfConnectionDetails, nfPolicyManagement, nfRemoteProcessGroup, nfLabel, nfProcessor, nfRemoteProcessGroupPorts, nfComponentVersion, nfQueueListing, nfStatusHistory) {
+}(this, function ($, d3, nfCanvasUtils, nfCommon, nfDialog, nfClient, nfErrorHandler, nfClipboard, nfSnippet, nfGoto, nfNgBridge, nfShell, nfVariableRegistry, nfComponentState, nfDraggable, nfBirdseye, nfConnection, nfGraph, nfProcessGroupConfiguration, nfProcessorConfiguration, nfProcessorDetails, nfLabelConfiguration, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupDetails, nfPortConfiguration, nfPortDetails, nfConnectionConfiguration, nfConnectionDetails, nfPolicyManagement, nfRemoteProcessGroup, nfLabel, nfProcessor, nfRemoteProcessGroupPorts, nfComponentVersion, nfQueueListing, nfStatusHistory) {
'use strict';
var config = {
@@ -1234,6 +1237,22 @@
},
/**
+ * Opens the variable registry for the specified selection of the current group if the selection is emtpy.
+ *
+ * @param {selection} selection
+ */
+ openVariableRegistry: function (selection) {
+ if (selection.empty()) {
+ nfVariableRegistry.showVariables(nfCanvasUtils.getGroupId());
+ } else if (selection.size() === 1) {
+ var selectionData = selection.datum();
+ if (nfCanvasUtils.isProcessGroup(selection)) {
+ nfVariableRegistry.showVariables(selectionData.id);
+ }
+ }
+ },
+
+ /**
* Views the state for the specified processor.
*
* @param {selection} selection
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
index 77ef67a..e3ab5c9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
@@ -37,6 +37,7 @@
'nf.Snippet',
'nf.Actions',
'nf.QueueListing',
+ 'nf.VariableRegistry',
'nf.ComponentState',
'nf.ComponentVersion',
'nf.Draggable',
@@ -81,8 +82,8 @@
'nf.ng.Canvas.OperateCtrl',
'nf.ng.BreadcrumbsDirective',
'nf.ng.DraggableDirective'],
- function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfClusterSummary, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfSettings, nfActions, nfSnippet, nfQueueListing, nfComponentState, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessGroupConfiguration, nfControllerServices, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupPorts, nfPortConfiguration, nfLabelConfiguration, nfProcessorDetails, nfPortDetails, nfConnectionDetails, nfRemoteProcessGroupDetails, nfGoto, nfNgBridge, appCtrl, appConfig, serviceProvider, breadcrumbsCtrl, headerCtrl, flowStatusCtrl, globalMenuCtrl, toolboxCtrl, processorComponent, inputPortComponent, outputPortComponent, processGroupComponent, remoteProcessGroupComponent, funnelComponent, templateComponent, labelComponent, graphControlsCtrl, nav
igateCtrl, operateCtrl, breadcrumbsDirective, draggableDirective) {
- return factory($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfClusterSummary, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfSettings, nfActions, nfSnippet, nfQueueListing, nfComponentState, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessGroupConfiguration, nfControllerServices, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupPorts, nfPortConfiguration, nfLabelConfiguration, nfProcessorDetails, nfPortDetails, nfConnectionDetails, nfRemoteProcessGroupDetails, nfGoto, nfNgBridge, appCtrl, appConfig, serviceProvider, breadcrumbsCtrl, headerCtrl, flowStatusCtrl, globalMenuCtrl, toolboxCtrl, processorComponent, inputPortComponent, outputPortComponent, processGroupComponent, remoteProcessGroupComponent, funnelComponent, templateComponent, labelComponent, graphControls
Ctrl, navigateCtrl, operateCtrl, breadcrumbsDirective, draggableDirective);
+ function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfClusterSummary, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVariableRegistry, nfComponentState, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessGroupConfiguration, nfControllerServices, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupPorts, nfPortConfiguration, nfLabelConfiguration, nfProcessorDetails, nfPortDetails, nfConnectionDetails, nfRemoteProcessGroupDetails, nfGoto, nfNgBridge, appCtrl, appConfig, serviceProvider, breadcrumbsCtrl, headerCtrl, flowStatusCtrl, globalMenuCtrl, toolboxCtrl, processorComponent, inputPortComponent, outputPortComponent, processGroupComponent, remoteProcessGroupComponent, funnelComponent, templateComponent, labelComponent, gr
aphControlsCtrl, navigateCtrl, operateCtrl, breadcrumbsDirective, draggableDirective) {
+ return factory($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfClusterSummary, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVariableRegistry, nfComponentState, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessGroupConfiguration, nfControllerServices, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupPorts, nfPortConfiguration, nfLabelConfiguration, nfProcessorDetails, nfPortDetails, nfConnectionDetails, nfRemoteProcessGroupDetails, nfGoto, nfNgBridge, appCtrl, appConfig, serviceProvider, breadcrumbsCtrl, headerCtrl, flowStatusCtrl, globalMenuCtrl, toolboxCtrl, processorComponent, inputPortComponent, outputPortComponent, processGroupComponent, remoteProcessGroupComponent, funnelComponent, templateComponent, labelComp
onent, graphControlsCtrl, navigateCtrl, operateCtrl, breadcrumbsDirective, draggableDirective);
});
} else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = factory(require('jquery'),
@@ -103,6 +104,7 @@
require('nf.Actions'),
require('nf.Snippet'),
require('nf.QueueListing'),
+ require('nf.VariableRegistry'),
require('nf.ComponentState'),
require('nf.ComponentVersion'),
require('nf.Draggable'),
@@ -166,6 +168,7 @@
root.nf.Actions,
root.nf.Snippet,
root.nf.QueueListing,
+ root.nf.VariableRegistry,
root.nf.ComponentState,
root.nf.ComponentVersion,
root.nf.Draggable,
@@ -211,7 +214,7 @@
root.nf.ng.BreadcrumbsDirective,
root.nf.ng.DraggableDirective);
}
-}(this, function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfClusterSummary, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfSettings, nfActions, nfSnippet, nfQueueListing, nfComponentState, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessGroupConfiguration, nfControllerServices, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupPorts, nfPortConfiguration, nfLabelConfiguration, nfProcessorDetails, nfPortDetails, nfConnectionDetails, nfRemoteProcessGroupDetails, nfGoto, nfNgBridge, appCtrl, appConfig, serviceProvider, breadcrumbsCtrl, headerCtrl, flowStatusCtrl, globalMenuCtrl, toolboxCtrl, processorComponent, inputPortComponent, outputPortComponent, processGroupComponent, remoteProcessGroupComponent, funnelComponent, templateComponent, labelComponent, graphControlsCtrl, navigat
eCtrl, operateCtrl, breadcrumbsDirective, draggableDirective) {
+}(this, function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfClusterSummary, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVariableRegistry, nfComponentState, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessGroupConfiguration, nfControllerServices, nfRemoteProcessGroupConfiguration, nfRemoteProcessGroupPorts, nfPortConfiguration, nfLabelConfiguration, nfProcessorDetails, nfPortDetails, nfConnectionDetails, nfRemoteProcessGroupDetails, nfGoto, nfNgBridge, appCtrl, appConfig, serviceProvider, breadcrumbsCtrl, headerCtrl, flowStatusCtrl, globalMenuCtrl, toolboxCtrl, processorComponent, inputPortComponent, outputPortComponent, processGroupComponent, remoteProcessGroupComponent, funnelComponent, templateComponent, labelComponent, graphC
ontrolsCtrl, navigateCtrl, operateCtrl, breadcrumbsDirective, draggableDirective) {
var config = {
urls: {
@@ -347,6 +350,7 @@
nfSettings.init();
nfActions.init();
nfQueueListing.init();
+ nfVariableRegistry.init();
nfComponentState.init();
nfComponentVersion.init(nfSettings);
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index 54f1d14..4ead97b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -239,6 +239,25 @@
},
/**
+ * Queries for bulletins for the specified components.
+ *
+ * @param {array} componentIds
+ * @returns {deferred}
+ */
+ queryBulletins: function (componentIds) {
+ var ids = componentIds.join('|');
+
+ return $.ajax({
+ type: 'GET',
+ url: '../nifi-api/flow/bulletin-board',
+ data: {
+ sourceId: ids
+ },
+ dataType: 'json'
+ }).fail(nfErrorHandler.handleAjaxError);
+ },
+
+ /**
* Shows the specified component in the specified group.
*
* @param {string} groupId The id of the group
@@ -282,6 +301,8 @@
});
}
});
+
+ return refreshGraph;
}
},
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
index 65346b2..0ebcf6a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
@@ -65,6 +65,15 @@
};
/**
+ * Determines whether the component in the specified selection has variables.
+ *
+ * @param {selection} selection The selection of currently selected components
+ */
+ var hasVariables = function (selection) {
+ return selection.empty() || nfCanvasUtils.isProcessGroup(selection);
+ };
+
+ /**
* Determines whether the component in the specified selection has configuration details.
*
* @param {selection} selection The selection of currently selected components
@@ -537,6 +546,7 @@
{separator: true},
{id: 'show-configuration-menu-item', condition: isConfigurable, menuItem: {clazz: 'fa fa-gear', text: 'Configure', action: 'showConfiguration'}},
{id: 'show-details-menu-item', condition: hasDetails, menuItem: {clazz: 'fa fa-gear', text: 'View configuration', action: 'showDetails'}},
+ {id: 'variable-registry-menu-item', condition: hasVariables, menuItem: {clazz: 'fa', text: 'Variables', action: 'openVariableRegistry'}},
{separator: true},
{id: 'enter-group-menu-item', condition: isProcessGroup, menuItem: {clazz: 'fa fa-sign-in', text: 'Enter group', action: 'enterGroup'}},
{separator: true},
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
index 90324bf..c7d9c43 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -586,7 +586,7 @@
});
// query for the bulletins
- queryBulletins(referencingComponentIds).done(function (response) {
+ nfCanvasUtils.queryBulletins(referencingComponentIds).done(function (response) {
var bulletins = response.bulletinBoard.bulletins;
updateReferencingComponentBulletins(bulletins);
});
@@ -623,25 +623,6 @@
};
/**
- * Queries for bulletins for the specified components.
- *
- * @param {array} componentIds
- * @returns {deferred}
- */
- var queryBulletins = function (componentIds) {
- var ids = componentIds.join('|');
-
- return $.ajax({
- type: 'GET',
- url: '../nifi-api/flow/bulletin-board',
- data: {
- sourceId: ids
- },
- dataType: 'json'
- }).fail(nfErrorHandler.handleAjaxError);
- };
-
- /**
* Sets whether the specified controller service is enabled.
*
* @param {jQuery} serviceTable
@@ -688,7 +669,7 @@
return service.state === 'DISABLED';
}
}, function (service) {
- return queryBulletins([service.id]);
+ return nfCanvasUtils.queryBulletins([service.id]);
}, pollCondition);
// once the service has updated, resolve and render the updated service
@@ -961,7 +942,7 @@
}
});
- return queryBulletins(referencingSchedulableComponents);
+ return nfCanvasUtils.queryBulletins(referencingSchedulableComponents);
}, pollCondition);
};
@@ -1006,7 +987,7 @@
}
});
- return queryBulletins(referencingSchedulableComponents);
+ return nfCanvasUtils.queryBulletins(referencingSchedulableComponents);
}, pollCondition);
};
@@ -1051,7 +1032,7 @@
}
});
- return queryBulletins(referencingSchedulableComponents);
+ return nfCanvasUtils.queryBulletins(referencingSchedulableComponents);
}, pollCondition);
};
@@ -1164,7 +1145,7 @@
$('#disable-controller-service-dialog').modal('setButtonModel', buttons).modal('show');
// load the bulletins
- queryBulletins([controllerService.id]).done(function (response) {
+ nfCanvasUtils.queryBulletins([controllerService.id]).done(function (response) {
updateBulletins(response.bulletinBoard.bulletins, $('#disable-controller-service-bulletins'));
});
@@ -1216,7 +1197,7 @@
$('#enable-controller-service-dialog').modal('setButtonModel', buttons).modal('show');
// load the bulletins
- queryBulletins([controllerService.id]).done(function (response) {
+ nfCanvasUtils.queryBulletins([controllerService.id]).done(function (response) {
updateBulletins(response.bulletinBoard.bulletins, $('#enable-controller-service-bulletins'));
});
[4/4] nifi git commit: NIFI-4280: - Adding support for the user to
configure variables in the UI. - Updating the endpoints for changing
variables as necessary. This closes #2135.
Posted by ma...@apache.org.
NIFI-4280:
- Adding support for the user to configure variables in the UI.
- Updating the endpoints for changing variables as necessary.
This closes #2135.
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/eac47e90
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/eac47e90
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/eac47e90
Branch: refs/heads/master
Commit: eac47e90cbfa39a8e40512379b02ff745262b5af
Parents: 9138326
Author: Matt Gilman <ma...@gmail.com>
Authored: Thu Aug 17 16:51:23 2017 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Sep 14 11:12:54 2017 -0400
----------------------------------------------------------------------
.../nifi/web/api/dto/AffectedComponentDTO.java | 92 +-
...ontrollerServiceReferencingComponentDTO.java | 8 +-
.../apache/nifi/web/api/dto/VariableDTO.java | 13 +-
.../nifi/web/api/dto/VariableRegistryDTO.java | 16 +-
.../dto/VariableRegistryUpdateRequestDTO.java | 27 +-
.../web/api/entity/AffectedComponentEntity.java | 44 +
.../web/api/entity/VariableRegistryEntity.java | 13 +-
.../VariableRegistryUpdateRequestEntity.java | 15 +-
.../http/StandardHttpResponseMapper.java | 21 +-
.../VariableRegistryEndpointMerger.java | 113 ++
.../manager/AffectedComponentEntityMerger.java | 102 ++
.../nifi/groups/StandardProcessGroup.java | 58 +-
.../variable/VariableRegistryUpdateRequest.java | 32 +-
.../org/apache/nifi/web/NiFiServiceFacade.java | 31 +-
.../nifi/web/StandardNiFiServiceFacade.java | 38 +-
.../nifi/web/api/ProcessGroupResource.java | 690 +++++---
.../org/apache/nifi/web/api/dto/DtoFactory.java | 148 +-
.../apache/nifi/web/api/dto/EntityFactory.java | 15 +
.../org/apache/nifi/web/dao/ProcessorDAO.java | 3 +-
.../nifi/web/dao/impl/StandardProcessorDAO.java | 9 +-
.../nifi-framework/nifi-web/nifi-web-ui/pom.xml | 1 +
.../main/resources/filters/canvas.properties | 1 +
.../src/main/webapp/WEB-INF/pages/canvas.jsp | 1 +
.../partials/canvas/variable-configuration.jsp | 93 +
.../src/main/webapp/css/controller-service.css | 4 +
.../nifi-web-ui/src/main/webapp/css/dialog.css | 51 +
.../propertytable/jquery.propertytable.js | 16 +-
.../src/main/webapp/js/nf/canvas/nf-actions.js | 25 +-
.../webapp/js/nf/canvas/nf-canvas-bootstrap.js | 10 +-
.../main/webapp/js/nf/canvas/nf-canvas-utils.js | 21 +
.../main/webapp/js/nf/canvas/nf-context-menu.js | 10 +
.../js/nf/canvas/nf-controller-service.js | 33 +-
.../webapp/js/nf/canvas/nf-variable-registry.js | 1633 ++++++++++++++++++
33 files changed, 2897 insertions(+), 490 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java
index 5d631ed..28999a5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java
@@ -17,43 +17,101 @@
package org.apache.nifi.web.api.dto;
-import javax.xml.bind.annotation.XmlType;
-
import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlType;
+import java.util.Collection;
+
@XmlType(name = "affectedComponent")
public class AffectedComponentDTO {
public static final String COMPONENT_TYPE_PROCESSOR = "PROCESSOR";
public static final String COMPONENT_TYPE_CONTROLLER_SERVICE = "CONTROLLER_SERVICE";
- private String parentGroupId;
- private String componentId;
- private String componentType;
+ private String processGroupId;
+ private String id;
+ private String referenceType;
+ private String name;
+ private String state;
+ private Integer activeThreadCount;
+
+ private Collection<String> validationErrors;
@ApiModelProperty("The UUID of the Process Group that this component is in")
- public String getParentGroupId() {
- return parentGroupId;
+ public String getProcessGroupId() {
+ return processGroupId;
}
- public void setParentGroupId(final String parentGroupId) {
- this.parentGroupId = parentGroupId;
+ public void setProcessGroupId(final String processGroupId) {
+ this.processGroupId = processGroupId;
}
@ApiModelProperty("The UUID of this component")
- public String getComponentId() {
- return componentId;
+ public String getId() {
+ return id;
}
- public void setComponentId(final String componentId) {
- this.componentId = componentId;
+ public void setId(final String id) {
+ this.id = id;
}
@ApiModelProperty(value = "The type of this component", allowableValues = COMPONENT_TYPE_PROCESSOR + "," + COMPONENT_TYPE_CONTROLLER_SERVICE)
- public String getComponentType() {
- return componentType;
+ public String getReferenceType() {
+ return referenceType;
+ }
+
+ public void setReferenceType(final String referenceType) {
+ this.referenceType = referenceType;
+ }
+
+ @ApiModelProperty("The name of this component.")
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return scheduled state of the processor referencing a controller service. If this component is another service, this field represents the controller service state
+ */
+ @ApiModelProperty(
+ value = "The scheduled state of a processor or reporting task referencing a controller service. If this component is another controller "
+ + "service, this field represents the controller service state."
+ )
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ /**
+ * @return active thread count for the referencing component
+ */
+ @ApiModelProperty(
+ value = "The number of active threads for the referencing component."
+ )
+ public Integer getActiveThreadCount() {
+ return activeThreadCount;
+ }
+
+ public void setActiveThreadCount(Integer activeThreadCount) {
+ this.activeThreadCount = activeThreadCount;
+ }
+
+ /**
+ * @return Any validation error associated with this component
+ */
+ @ApiModelProperty(
+ value = "The validation errors for the component."
+ )
+ public Collection<String> getValidationErrors() {
+ return validationErrors;
}
- public void setComponentType(final String componentType) {
- this.componentType = componentType;
+ public void setValidationErrors(Collection<String> validationErrors) {
+ this.validationErrors = validationErrors;
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
index 380e6ce..6279ac3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
@@ -19,10 +19,10 @@ package org.apache.nifi.web.api.dto;
import com.wordnik.swagger.annotations.ApiModelProperty;
import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentEntity;
+import javax.xml.bind.annotation.XmlType;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
-import javax.xml.bind.annotation.XmlType;
/**
* A component referencing a controller service. This can either be another controller service or a processor. Depending on the type of component different properties may be set.
@@ -105,11 +105,11 @@ public class ControllerServiceReferencingComponentDTO {
}
/**
- * @return state of the processor referencing a controller service. If this component is another service, this field is blank
+ * @return scheduled state of the processor referencing a controller service. If this component is another service, this field represents the controller service state
*/
@ApiModelProperty(
- value = "The state of a processor or reporting task referencing a controller service. If this component is another controller "
- + "service, this field is blank."
+ value = "The scheduled state of a processor or reporting task referencing a controller service. If this component is another controller "
+ + "service, this field represents the controller service state."
)
public String getState() {
return state;
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java
index c686316..89ea27a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java
@@ -17,19 +17,18 @@
package org.apache.nifi.web.api.dto;
-import java.util.HashSet;
-import java.util.Set;
+import com.wordnik.swagger.annotations.ApiModelProperty;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import javax.xml.bind.annotation.XmlType;
-
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import java.util.Set;
@XmlType(name = "variable")
public class VariableDTO {
private String name;
private String value;
private String processGroupId;
- private Set<AffectedComponentDTO> affectedComponents = new HashSet<>();
+ private Set<AffectedComponentEntity> affectedComponents;
@ApiModelProperty("The name of the variable")
public String getName() {
@@ -59,11 +58,11 @@ public class VariableDTO {
}
@ApiModelProperty(value = "A set of all components that will be affected if the value of this variable is changed", readOnly = true)
- public Set<AffectedComponentDTO> getAffectedComponents() {
+ public Set<AffectedComponentEntity> getAffectedComponents() {
return affectedComponents;
}
- public void setAffectedComponents(Set<AffectedComponentDTO> affectedComponents) {
+ public void setAffectedComponents(Set<AffectedComponentEntity> affectedComponents) {
this.affectedComponents = affectedComponents;
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java
index c106a9a..8f37532 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java
@@ -17,18 +17,16 @@
package org.apache.nifi.web.api.dto;
-import java.util.Set;
-
-import javax.xml.bind.annotation.XmlType;
-
+import com.wordnik.swagger.annotations.ApiModelProperty;
import org.apache.nifi.web.api.entity.VariableEntity;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlType;
+import java.util.Set;
@XmlType(name = "variableRegistry")
public class VariableRegistryDTO {
private Set<VariableEntity> variables;
- private String groupId;
+ private String processGroupId;
public void setVariables(final Set<VariableEntity> variables) {
this.variables = variables;
@@ -39,12 +37,12 @@ public class VariableRegistryDTO {
return variables;
}
- public void setProcessGroupId(final String groupId) {
- this.groupId = groupId;
+ public void setProcessGroupId(final String processGroupId) {
+ this.processGroupId = processGroupId;
}
@ApiModelProperty("The UUID of the Process Group that this Variable Registry belongs to")
public String getProcessGroupId() {
- return groupId;
+ return processGroupId;
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java
index 06a0dc2..e57e30a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java
@@ -17,27 +17,27 @@
package org.apache.nifi.web.api.dto;
-import java.util.Date;
-import java.util.List;
+import com.wordnik.swagger.annotations.ApiModelProperty;
+import org.apache.nifi.web.api.dto.util.TimestampAdapter;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-import org.apache.nifi.web.api.dto.util.TimestampAdapter;
-
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
@XmlType(name = "variableRegistryUpdateRequest")
public class VariableRegistryUpdateRequestDTO {
private String requestId;
private String processGroupId;
private String uri;
- private Date submissionTime = new Date();
- private Date lastUpdated = new Date();
+ private Date submissionTime;
+ private Date lastUpdated;
private boolean complete = false;
private String failureReason;
private List<VariableRegistryUpdateStepDTO> updateSteps;
-
+ private Set<AffectedComponentEntity> affectedComponents;
@ApiModelProperty("The unique ID of the Process Group that the variable registry belongs to")
public String getProcessGroupId() {
@@ -112,4 +112,13 @@ public class VariableRegistryUpdateRequestDTO {
public void setFailureReason(String reason) {
this.failureReason = reason;
}
+
+ @ApiModelProperty(value = "A set of all components that will be affected if the value of this variable is changed", readOnly = true)
+ public Set<AffectedComponentEntity> getAffectedComponents() {
+ return affectedComponents;
+ }
+
+ public void setAffectedComponents(Set<AffectedComponentEntity> affectedComponents) {
+ this.affectedComponents = affectedComponents;
+ }
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AffectedComponentEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AffectedComponentEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AffectedComponentEntity.java
new file mode 100644
index 0000000..0f28f73
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AffectedComponentEntity.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.web.api.entity;
+
+import org.apache.nifi.web.api.dto.AffectedComponentDTO;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a response to the API.
+ * This particular entity holds a reference to component that references a variable.
+ */
+@XmlRootElement(name = "affectComponentEntity")
+public class AffectedComponentEntity extends ComponentEntity implements Permissible<AffectedComponentDTO> {
+
+ private AffectedComponentDTO component;
+
+ /**
+ * @return variable referencing components that is being serialized
+ */
+ public AffectedComponentDTO getComponent() {
+ return component;
+ }
+
+ public void setComponent(AffectedComponentDTO component) {
+ this.component = component;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java
index d876453..1b7da0c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java
@@ -17,16 +17,15 @@
package org.apache.nifi.web.api.entity;
-import javax.xml.bind.annotation.XmlRootElement;
-
+import com.wordnik.swagger.annotations.ApiModelProperty;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.VariableRegistryDTO;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "variableRegistryEntity")
public class VariableRegistryEntity extends Entity {
- private RevisionDTO groupRevision;
+ private RevisionDTO processGroupRevision;
private VariableRegistryDTO variableRegistry;
@@ -41,10 +40,10 @@ public class VariableRegistryEntity extends Entity {
@ApiModelProperty("The revision of the Process Group that the Variable Registry belongs to")
public RevisionDTO getProcessGroupRevision() {
- return groupRevision;
+ return processGroupRevision;
}
- public void setProcessGroupRevision(RevisionDTO revision) {
- this.groupRevision = revision;
+ public void setProcessGroupRevision(RevisionDTO processGroupRevision) {
+ this.processGroupRevision = processGroupRevision;
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java
index 77257af..bfeb9ce 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java
@@ -17,16 +17,15 @@
package org.apache.nifi.web.api.entity;
-import javax.xml.bind.annotation.XmlRootElement;
-
+import com.wordnik.swagger.annotations.ApiModelProperty;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.VariableRegistryUpdateRequestDTO;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "variableRegistryUpdateRequestEntity")
public class VariableRegistryUpdateRequestEntity extends Entity {
- private VariableRegistryUpdateRequestDTO requestDto;
+ private VariableRegistryUpdateRequestDTO request;
private RevisionDTO processGroupRevision;
@ApiModelProperty("The revision for the Process Group that owns this variable registry.")
@@ -39,11 +38,11 @@ public class VariableRegistryUpdateRequestEntity extends Entity {
}
@ApiModelProperty("The Variable Registry Update Request")
- public VariableRegistryUpdateRequestDTO getRequestDto() {
- return requestDto;
+ public VariableRegistryUpdateRequestDTO getRequest() {
+ return request;
}
- public void setRequestDto(VariableRegistryUpdateRequestDTO requestDto) {
- this.requestDto = requestDto;
+ public void setRequest(VariableRegistryUpdateRequestDTO request) {
+ this.request = request;
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java
index c102746..fa23603 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java
@@ -16,16 +16,6 @@
*/
package org.apache.nifi.cluster.coordination.http;
-import java.io.IOException;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import javax.ws.rs.core.StreamingOutput;
-
import org.apache.nifi.cluster.coordination.http.endpoints.AccessPolicyEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.BulletinBoardEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.ComponentStateEndpointMerger;
@@ -79,6 +69,7 @@ import org.apache.nifi.cluster.coordination.http.endpoints.UserEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.UserGroupEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.UserGroupsEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.UsersEndpointMerger;
+import org.apache.nifi.cluster.coordination.http.endpoints.VariableRegistryEndpointMerger;
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.stream.io.NullOutputStream;
@@ -87,6 +78,15 @@ import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.ws.rs.core.StreamingOutput;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
public class StandardHttpResponseMapper implements HttpResponseMapper {
private Logger logger = LoggerFactory.getLogger(StandardHttpResponseMapper.class);
@@ -154,6 +154,7 @@ public class StandardHttpResponseMapper implements HttpResponseMapper {
endpointMergers.add(new UserGroupEndpointMerger());
endpointMergers.add(new AccessPolicyEndpointMerger());
endpointMergers.add(new SearchUsersEndpointMerger());
+ endpointMergers.add(new VariableRegistryEndpointMerger());
}
@Override
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/VariableRegistryEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/VariableRegistryEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/VariableRegistryEndpointMerger.java
new file mode 100644
index 0000000..0420911
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/VariableRegistryEndpointMerger.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.AffectedComponentEntityMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.VariableDTO;
+import org.apache.nifi.web.api.dto.VariableRegistryDTO;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
+import org.apache.nifi.web.api.entity.VariableEntity;
+import org.apache.nifi.web.api.entity.VariableRegistryEntity;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class VariableRegistryEndpointMerger extends AbstractSingleEntityEndpoint<VariableRegistryEntity> implements EndpointResponseMerger {
+ public static final Pattern VARIABLE_REGISTRY_UPDATE_REQUEST_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/variable-registry");
+
+ private final AffectedComponentEntityMerger affectedComponentEntityMerger = new AffectedComponentEntityMerger();
+
+ @Override
+ public boolean canHandle(final URI uri, final String method) {
+ if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (VARIABLE_REGISTRY_UPDATE_REQUEST_URI_PATTERN.matcher(uri.getPath()).matches())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected Class<VariableRegistryEntity> getEntityClass() {
+ return VariableRegistryEntity.class;
+ }
+
+ @Override
+ protected void mergeResponses(final VariableRegistryEntity clientEntity, final Map<NodeIdentifier, VariableRegistryEntity> entityMap,
+ final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
+
+ final VariableRegistryDTO clientVariableRegistry = clientEntity.getVariableRegistry();
+ final Set<VariableEntity> clientVariableEntities = clientVariableRegistry.getVariables();
+
+ if (clientVariableEntities != null) {
+ for (final Iterator<VariableEntity> i = clientVariableEntities.iterator(); i.hasNext();) {
+ final VariableEntity clientVariableEntity = i.next();
+ final VariableDTO clientVariable = clientVariableEntity.getVariable();
+
+ final Map<NodeIdentifier, Set<AffectedComponentEntity>> nodeAffectedComponentEntities = new HashMap<>();
+
+ boolean retainClientVariable = true;
+ for (final Map.Entry<NodeIdentifier, VariableRegistryEntity> nodeEntry : entityMap.entrySet()) {
+ final VariableRegistryEntity nodeVariableRegistry = nodeEntry.getValue();
+ final Set<VariableEntity> nodeVariableEntities = nodeVariableRegistry.getVariableRegistry().getVariables();
+
+ // if this node has no variables, then the current client variable should be removed
+ if (nodeVariableEntities == null) {
+ retainClientVariable = false;
+ break;
+ }
+
+ boolean variableFound = false;
+ for (final VariableEntity nodeVariableEntity : nodeVariableEntities) {
+ final VariableDTO nodeVariable = nodeVariableEntity.getVariable();
+
+ // identify the current clientVariable for each node
+ if (clientVariable.getProcessGroupId().equals(nodeVariable.getProcessGroupId()) && clientVariable.getName().equals(nodeVariable.getName())) {
+ variableFound = true;
+
+ if (Boolean.FALSE.equals(nodeVariableEntity.getCanWrite())) {
+ clientVariableEntity.setCanWrite(false);
+ }
+
+ nodeAffectedComponentEntities.put(nodeEntry.getKey(), nodeVariableEntity.getVariable().getAffectedComponents());
+ break;
+ }
+ }
+
+ if (!variableFound) {
+ retainClientVariable = false;
+ break;
+ }
+ }
+
+ if (!retainClientVariable) {
+ i.remove();
+ } else {
+ final Set<AffectedComponentEntity> clientAffectedComponentEntities = clientVariableEntity.getVariable().getAffectedComponents();
+ affectedComponentEntityMerger.mergeAffectedComponents(clientAffectedComponentEntities, nodeAffectedComponentEntities);
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AffectedComponentEntityMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AffectedComponentEntityMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AffectedComponentEntityMerger.java
new file mode 100644
index 0000000..60a605d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AffectedComponentEntityMerger.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.web.api.dto.AffectedComponentDTO;
+import org.apache.nifi.web.api.dto.PermissionsDTO;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class AffectedComponentEntityMerger {
+
+ public void mergeAffectedComponents(final Set<AffectedComponentEntity> affectedComponents, final Map<NodeIdentifier, Set<AffectedComponentEntity>> affectedComponentMap) {
+
+ final Map<String, Integer> activeThreadCounts = new HashMap<>();
+ final Map<String, String> states = new HashMap<>();
+ final Map<String, PermissionsDTO> canReads = new HashMap<>();
+
+ for (final Map.Entry<NodeIdentifier, Set<AffectedComponentEntity>> nodeEntry : affectedComponentMap.entrySet()) {
+ final Set<AffectedComponentEntity> nodeAffectedComponents = nodeEntry.getValue();
+
+ // go through all the nodes referencing components
+ if (nodeAffectedComponents != null) {
+ for (final AffectedComponentEntity nodeAffectedComponentEntity : nodeAffectedComponents) {
+ final AffectedComponentDTO nodeAffectedComponent = nodeAffectedComponentEntity.getComponent();
+
+ if (nodeAffectedComponentEntity.getPermissions().getCanRead()) {
+ // handle active thread counts
+ if (nodeAffectedComponent.getActiveThreadCount() != null && nodeAffectedComponent.getActiveThreadCount() > 0) {
+ final Integer current = activeThreadCounts.get(nodeAffectedComponent.getId());
+ if (current == null) {
+ activeThreadCounts.put(nodeAffectedComponent.getId(), nodeAffectedComponent.getActiveThreadCount());
+ } else {
+ activeThreadCounts.put(nodeAffectedComponent.getId(), nodeAffectedComponent.getActiveThreadCount() + current);
+ }
+ }
+
+ // handle controller service state
+ final String state = states.get(nodeAffectedComponent.getId());
+ if (state == null) {
+ if (ControllerServiceState.DISABLING.name().equals(nodeAffectedComponent.getState())) {
+ states.put(nodeAffectedComponent.getId(), ControllerServiceState.DISABLING.name());
+ } else if (ControllerServiceState.ENABLING.name().equals(nodeAffectedComponent.getState())) {
+ states.put(nodeAffectedComponent.getId(), ControllerServiceState.ENABLING.name());
+ }
+ }
+ }
+
+ // handle read permissions
+ final PermissionsDTO mergedPermissions = canReads.get(nodeAffectedComponentEntity.getId());
+ final PermissionsDTO permissions = nodeAffectedComponentEntity.getPermissions();
+ if (permissions != null) {
+ if (mergedPermissions == null) {
+ canReads.put(nodeAffectedComponentEntity.getId(), permissions);
+ } else {
+ PermissionsDtoMerger.mergePermissions(mergedPermissions, permissions);
+ }
+ }
+ }
+ }
+ }
+
+ // go through each affected components
+ if (affectedComponents != null) {
+ for (final AffectedComponentEntity affectedComponent : affectedComponents) {
+ final PermissionsDTO permissions = canReads.get(affectedComponent.getId());
+ if (permissions != null && permissions.getCanRead() != null && permissions.getCanRead()) {
+ final Integer activeThreadCount = activeThreadCounts.get(affectedComponent.getId());
+ if (activeThreadCount != null) {
+ affectedComponent.getComponent().setActiveThreadCount(activeThreadCount);
+ }
+
+ final String state = states.get(affectedComponent.getId());
+ if (state != null) {
+ affectedComponent.getComponent().setState(state);
+ }
+ } else {
+ affectedComponent.setPermissions(permissions);
+ affectedComponent.setComponent(null);
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 2b7b51d..1754cf7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -16,23 +16,7 @@
*/
package org.apache.nifi.groups;
-import static java.util.Objects.requireNonNull;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.stream.Collectors;
-
+import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -76,6 +60,7 @@ import org.apache.nifi.logging.LogRepositoryFactory;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarCloseable;
import org.apache.nifi.processor.StandardProcessContext;
+import org.apache.nifi.registry.ComponentVariableRegistry;
import org.apache.nifi.registry.VariableDescriptor;
import org.apache.nifi.registry.variable.MutableVariableRegistry;
import org.apache.nifi.remote.RemoteGroupPort;
@@ -87,7 +72,22 @@ import org.apache.nifi.web.api.dto.TemplateDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
public final class StandardProcessGroup implements ProcessGroup {
@@ -2651,7 +2651,7 @@ public final class StandardProcessGroup implements ProcessGroup {
final Set<ConfiguredComponent> affected = new HashSet<>();
// Determine any Processors that references the variable
- for (final ProcessorNode processor : findAllProcessors()) {
+ for (final ProcessorNode processor : getProcessors()) {
for (final VariableImpact impact : getVariableImpact(processor)) {
if (impact.isImpacted(variableName)) {
affected.add(processor);
@@ -2662,7 +2662,7 @@ public final class StandardProcessGroup implements ProcessGroup {
// Determine any Controller Service that references the variable. If Service A references a variable,
// then that means that any other component that references that service is also affected, so recursively
// find any references to that service and add it.
- for (final ControllerServiceNode service : findAllControllerServices()) {
+ for (final ControllerServiceNode service : getControllerServices(false)) {
for (final VariableImpact impact : getVariableImpact(service)) {
if (impact.isImpacted(variableName)) {
affected.add(service);
@@ -2673,6 +2673,18 @@ public final class StandardProcessGroup implements ProcessGroup {
}
}
+ // For any child Process Group that does not override the variable, also include its references.
+ // If a child group has a value for the same variable, though, then that means that the child group
+ // is overriding the variable and its components are actually referencing a different variable.
+ for (final ProcessGroup childGroup : getProcessGroups()) {
+ final ComponentVariableRegistry childRegistry = childGroup.getVariableRegistry();
+ final VariableDescriptor descriptor = childRegistry.getVariableKey(variableName);
+ final boolean overridden = childRegistry.getVariableMap().containsKey(descriptor);
+ if (!overridden) {
+ affected.addAll(childGroup.getComponentsAffectedByVariable(variableName));
+ }
+ }
+
return affected;
}
@@ -2695,7 +2707,11 @@ public final class StandardProcessGroup implements ProcessGroup {
}
private List<VariableImpact> getVariableImpact(final ConfiguredComponent component) {
- return component.getProperties().values().stream()
+ return component.getProperties().keySet().stream()
+ .map(descriptor -> {
+ final String configuredVal = component.getProperty(descriptor);
+ return configuredVal == null ? descriptor.getDefaultValue() : configuredVal;
+ })
.map(propVal -> Query.prepare(propVal).getVariableImpact())
.collect(Collectors.toList());
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateRequest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateRequest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateRequest.java
index 82d4683..72f9a7a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateRequest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateRequest.java
@@ -17,17 +17,28 @@
package org.apache.nifi.registry.variable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
+
import java.util.Date;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+import java.util.stream.Collectors;
public class VariableRegistryUpdateRequest {
private final String requestId;
private final String processGroupId;
+ private final NiFiUser user;
private volatile Date submissionTime = new Date();
private volatile Date lastUpdated = new Date();
private volatile boolean complete = false;
private final AtomicReference<String> failureReason = new AtomicReference<>();
+ private RevisionDTO processGroupRevision;
+ private Map<String, AffectedComponentEntity> affectedComponents;
private final VariableRegistryUpdateStep identifyComponentsStep = new VariableRegistryUpdateStep("Identifying components affected");
private final VariableRegistryUpdateStep stopProcessors = new VariableRegistryUpdateStep("Stopping affected Processors");
@@ -36,16 +47,17 @@ public class VariableRegistryUpdateRequest {
private final VariableRegistryUpdateStep enableServices = new VariableRegistryUpdateStep("Re-Enabling affected Controller Services");
private final VariableRegistryUpdateStep startProcessors = new VariableRegistryUpdateStep("Restarting affected Processors");
- public VariableRegistryUpdateRequest(final String requestId, final String processGroupId) {
+ public VariableRegistryUpdateRequest(final String requestId, final String processGroupId, final Set<AffectedComponentEntity> affectedComponents, final NiFiUser user) {
this.requestId = requestId;
this.processGroupId = processGroupId;
+ this.affectedComponents = affectedComponents.stream().collect(Collectors.toMap(AffectedComponentEntity::getId, Function.identity()));
+ this.user = user;
}
public String getProcessGroupId() {
return processGroupId;
}
-
public String getRequestId() {
return requestId;
}
@@ -54,6 +66,10 @@ public class VariableRegistryUpdateRequest {
return submissionTime;
}
+ public NiFiUser getUser() {
+ return user;
+ }
+
public Date getLastUpdated() {
return lastUpdated;
}
@@ -102,6 +118,18 @@ public class VariableRegistryUpdateRequest {
this.failureReason.set(reason);
}
+ public RevisionDTO getProcessGroupRevision() {
+ return processGroupRevision;
+ }
+
+ public void setProcessGroupRevision(RevisionDTO processGroupRevision) {
+ this.processGroupRevision = processGroupRevision;
+ }
+
+ public Map<String, AffectedComponentEntity> getAffectedComponents() {
+ return affectedComponents;
+ }
+
public void cancel() {
this.failureReason.compareAndSet(null, "Update was cancelled");
this.complete = true;
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 6fed58e..faa8c0e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -16,13 +16,6 @@
*/
package org.apache.nifi.web;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-
import org.apache.nifi.authorization.AuthorizeAccess;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.user.NiFiUser;
@@ -77,6 +70,7 @@ import org.apache.nifi.web.api.dto.status.ControllerStatusDTO;
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
import org.apache.nifi.web.api.entity.ActionEntity;
import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import org.apache.nifi.web.api.entity.BulletinEntity;
import org.apache.nifi.web.api.entity.ConnectionEntity;
import org.apache.nifi.web.api.entity.ConnectionStatusEntity;
@@ -108,6 +102,13 @@ import org.apache.nifi.web.api.entity.UserEntity;
import org.apache.nifi.web.api.entity.UserGroupEntity;
import org.apache.nifi.web.api.entity.VariableRegistryEntity;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
/**
* Defines the NiFiServiceFacade interface.
*/
@@ -518,9 +519,10 @@ public interface NiFiServiceFacade {
* Gets all the Processor transfer objects for this controller.
*
* @param groupId group
+ * @param includeDescendants if processors from descendent groups should be included
* @return List of all the Processor transfer object
*/
- Set<ProcessorEntity> getProcessors(String groupId);
+ Set<ProcessorEntity> getProcessors(String groupId, boolean includeDescendants);
/**
* Verifies the specified processor can be updated.
@@ -925,12 +927,21 @@ public interface NiFiServiceFacade {
VariableRegistryEntity updateVariableRegistry(NiFiUser user, Revision revision, VariableRegistryDTO variableRegistryDto);
/**
- * Determines which components will be affected by updating the given Variable Registry
+ * Determines which components will be affected by updating the given Variable Registry.
+ *
+ * @param variableRegistryDto the variable registry
+ * @return the components that will be affected
+ */
+ Set<AffectedComponentEntity> getComponentsAffectedByVariableRegistryUpdate(VariableRegistryDTO variableRegistryDto);
+
+ /**
+ * Determines which components are active and will be affected by updating the given Variable Registry. These active components
+ * are needed to authorize the request and deactivate prior to changing the variables.
*
* @param variableRegistryDto the variable registry
* @return the components that will be affected
*/
- Set<AffectedComponentDTO> getComponentsAffectedByVariableRegistryUpdate(VariableRegistryDTO variableRegistryDto);
+ Set<AffectedComponentDTO> getActiveComponentsAffectedByVariableRegistryUpdate(VariableRegistryDTO variableRegistryDto);
/**
* Gets all process groups in the specified parent group.
http://git-wip-us.apache.org/repos/asf/nifi/blob/eac47e90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
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 7cd5732..9caabd6 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
@@ -161,6 +161,7 @@ import org.apache.nifi.web.api.entity.AccessPolicyEntity;
import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
import org.apache.nifi.web.api.entity.ActionEntity;
import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
+import org.apache.nifi.web.api.entity.AffectedComponentEntity;
import org.apache.nifi.web.api.entity.BulletinEntity;
import org.apache.nifi.web.api.entity.ComponentReferenceEntity;
import org.apache.nifi.web.api.entity.ConnectionEntity;
@@ -790,7 +791,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
@Override
- public Set<AffectedComponentDTO> getComponentsAffectedByVariableRegistryUpdate(final VariableRegistryDTO variableRegistryDto) {
+ public Set<AffectedComponentDTO> getActiveComponentsAffectedByVariableRegistryUpdate(final VariableRegistryDTO variableRegistryDto) {
final ProcessGroup group = processGroupDAO.getProcessGroup(variableRegistryDto.getProcessGroupId());
if (group == null) {
throw new ResourceNotFoundException("Could not find Process Group with ID " + variableRegistryDto.getProcessGroupId());
@@ -827,6 +828,29 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
return affectedComponentDtos;
}
+ @Override
+ public Set<AffectedComponentEntity> getComponentsAffectedByVariableRegistryUpdate(final VariableRegistryDTO variableRegistryDto) {
+ final ProcessGroup group = processGroupDAO.getProcessGroup(variableRegistryDto.getProcessGroupId());
+ if (group == null) {
+ throw new ResourceNotFoundException("Could not find Process Group with ID " + variableRegistryDto.getProcessGroupId());
+ }
+
+ final Map<String, String> variableMap = new HashMap<>();
+ variableRegistryDto.getVariables().stream() // have to use forEach here instead of using Collectors.toMap because value may be null
+ .map(VariableEntity::getVariable)
+ .forEach(var -> variableMap.put(var.getName(), var.getValue()));
+
+ final Set<AffectedComponentEntity> affectedComponentEntities = new HashSet<>();
+
+ final Set<String> updatedVariableNames = getUpdatedVariables(group, variableMap);
+ for (final String variableName : updatedVariableNames) {
+ final Set<ConfiguredComponent> affectedComponents = group.getComponentsAffectedByVariable(variableName);
+ affectedComponentEntities.addAll(dtoFactory.createAffectedComponentEntities(affectedComponents, revisionManager));
+ }
+
+ return affectedComponentEntities;
+ }
+
private Set<String> getUpdatedVariables(final ProcessGroup group, final Map<String, String> newVariableValues) {
final Set<String> updatedVariableNames = new HashSet<>();
@@ -856,7 +880,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final RevisionUpdate<VariableRegistryDTO> snapshot = updateComponent(user, revision,
processGroupNode,
() -> processGroupDAO.updateVariableRegistry(variableRegistryDto),
- processGroup -> dtoFactory.createVariableRegistryDto(processGroup));
+ processGroup -> dtoFactory.createVariableRegistryDto(processGroup, revisionManager));
final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processGroupNode);
final RevisionDTO updatedRevision = dtoFactory.createRevisionDTO(snapshot.getLastModification());
@@ -2498,8 +2522,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
@Override
- public Set<ProcessorEntity> getProcessors(final String groupId) {
- final Set<ProcessorNode> processors = processorDAO.getProcessors(groupId);
+ public Set<ProcessorEntity> getProcessors(final String groupId, final boolean includeDescendants) {
+ final Set<ProcessorNode> processors = processorDAO.getProcessors(groupId, includeDescendants);
return processors.stream()
.map(processor -> createProcessorEntity(processor))
.collect(Collectors.toSet());
@@ -3231,7 +3255,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
private VariableRegistryEntity createVariableRegistryEntity(final ProcessGroup processGroup, final boolean includeAncestorGroups) {
- final VariableRegistryDTO registryDto = dtoFactory.createVariableRegistryDto(processGroup);
+ final VariableRegistryDTO registryDto = dtoFactory.createVariableRegistryDto(processGroup, revisionManager);
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(processGroup.getIdentifier()));
final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processGroup);
@@ -3240,7 +3264,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
while (parent != null) {
final PermissionsDTO parentPerms = dtoFactory.createPermissionsDto(parent);
if (Boolean.TRUE.equals(parentPerms.getCanRead())) {
- final VariableRegistryDTO parentRegistryDto = dtoFactory.createVariableRegistryDto(parent);
+ final VariableRegistryDTO parentRegistryDto = dtoFactory.createVariableRegistryDto(parent, revisionManager);
final Set<VariableEntity> parentVariables = parentRegistryDto.getVariables();
registryDto.getVariables().addAll(parentVariables);
}
@@ -3260,7 +3284,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
throw new ResourceNotFoundException("Could not find group with ID " + groupId);
}
- final VariableRegistryDTO registryDto = dtoFactory.populateAffectedComponents(variableRegistryDto, processGroup);
+ final VariableRegistryDTO registryDto = dtoFactory.populateAffectedComponents(variableRegistryDto, processGroup, revisionManager);
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(processGroup.getIdentifier()));
final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processGroup);
return entityFactory.createVariableRegistryEntity(registryDto, revision, permissions);