From 9507f2f8d2ec616f01f5ee8825106300b95e8ddc Mon Sep 17 00:00:00 2001 From: Andrew Gauld Date: Fri, 7 Feb 2020 15:00:39 +0000 Subject: Add DCAE MOD design tool project Change-Id: I660b28ebfaa7e4b5f03a1df5fd17d126f58b7c14 Issue-ID: DCAEGEN2-1860 Signed-off-by: Andrew Gauld --- .../main/webapp/js/nf/canvas/nf-process-group.js | 1744 ++++++++++++++++++++ 1 file changed, 1744 insertions(+) create mode 100644 mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js (limited to 'mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js') diff --git a/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js b/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js new file mode 100644 index 0000000..614472c --- /dev/null +++ b/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js @@ -0,0 +1,1744 @@ +/* + * 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. + * + * Modifications to the original nifi code for the ONAP project are made + * available under the Apache License, Version 2.0 + */ + +/* global d3, define, module, require, exports */ + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery', + 'd3', + 'nf.Connection', + 'nf.Common', + 'nf.Client', + 'nf.CanvasUtils', + 'nf.Dialog'], + function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) { + return (nf.ProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog)); + }); + } else if (typeof exports === 'object' && typeof module === 'object') { + module.exports = (nf.ProcessGroup = + factory(require('jquery'), + require('d3'), + require('nf.Connection'), + require('nf.Common'), + require('nf.Client'), + require('nf.CanvasUtils'), + require('nf.Dialog'))); + } else { + nf.ProcessGroup = factory(root.$, + root.d3, + root.nf.Connection, + root.nf.Common, + root.nf.Client, + root.nf.CanvasUtils, + root.nf.Dialog); + } +} +(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) { + 'use strict'; + + var nfConnectable; + var nfDraggable; + var nfSelectable; + var nfContextMenu; + + var PREVIEW_NAME_LENGTH = 30; + + var dimensions = { + width: 380, + height: 172 + }; + + // ---------------------------- + // process groups currently on the graph + // ---------------------------- + + var processGroupMap; + + // ----------------------------------------------------------- + // cache for components that are added/removed from the canvas + // ----------------------------------------------------------- + + var removedCache; + var addedCache; + + // -------------------- + // component containers + // -------------------- + + var processGroupContainer; + + // -------------------------- + // privately scoped functions + // -------------------------- + + /** + * Determines whether the specified process group is under version control. + * + * @param d + */ + var isUnderVersionControl = function (d) { + return nfCommon.isDefinedAndNotNull(d.versionedFlowState); + }; + + /** + * Selects the process group elements against the current process group map. + */ + var select = function () { + return processGroupContainer.selectAll('g.process-group').data(processGroupMap.values(), function (d) { + return d.id; + }); + }; + + + /** + * Renders the process groups in the specified selection. + * + * @param {selection} entered The selection of process groups to be rendered + * @param {boolean} selected Whether the process group should be selected + * @return the entered selection + */ + var renderProcessGroups = function (entered, selected) { + if (entered.empty()) { + return entered; + } + + var processGroup = entered.append('g') + .attrs({ + 'id': function (d) { + return 'id-' + d.id; + }, + 'class': 'process-group component' + }) + .classed('selected', selected) + .call(nfCanvasUtils.position); + + // ---- + // body + // ---- + + // process group border + processGroup.append('rect') + .attrs({ + 'class': 'border', + 'width': function (d) { + return d.dimensions.width; + }, + 'height': function (d) { + return d.dimensions.height; + }, + 'fill': 'transparent', + 'stroke': 'transparent' + }); + + // process group body + processGroup.append('rect') + .attrs({ + 'class': 'body', + 'width': function (d) { + return d.dimensions.width; + }, + 'height': function (d) { + return d.dimensions.height; + }, + 'filter': 'url(#component-drop-shadow)', + 'stroke-width': 0 + }); + + // process group name background + processGroup.append('rect') + .attrs({ + 'width': function (d) { + return d.dimensions.width; + }, + 'height': 32, + 'fill': '#b8c6cd' + }); + + // process group name + processGroup.append('text') + .attrs({ + 'x': 10, + 'y': 20, + 'width': 300, + 'height': 16, + 'class': 'process-group-name' + }); + + // process group name + processGroup.append('text') + .attrs({ + 'x': 10, + 'y': 21, + 'class': 'version-control' + }); + + console.log(processGroup); + + + // always support selecting and navigation + processGroup.on('dblclick', function (d) { + // enter this group on double click + nfProcessGroup.enterGroup(d.id); + }) + .call(nfSelectable.activate).call(nfContextMenu.activate); + + // only support dragging, connection, and drag and drop if appropriate + processGroup.filter(function (d) { + return d.permissions.canWrite && d.permissions.canRead; + }) + .on('mouseover.drop', function (d) { + // Using mouseover/out to workaround chrome issue #122746 + + // get the target and ensure its not already been marked for drop + var target = d3.select(this); + if (!target.classed('drop')) { + var targetData = target.datum(); + + // see if there is a selection being dragged + var drag = d3.select('rect.drag-selection'); + if (!drag.empty()) { + // filter the current selection by this group + var selection = nfCanvasUtils.getSelection().filter(function (d) { + return targetData.id === d.id; + }); + + // ensure this group isn't in the selection + if (selection.empty()) { + // mark that we are hovering over a drop area if appropriate + target.classed('drop', function () { + // get the current selection and ensure its disconnected + return nfConnection.isDisconnected(nfCanvasUtils.getSelection()); + }); + } + } + } + }) + .on('mouseout.drop', function (d) { + // mark that we are no longer hovering over a drop area unconditionally + d3.select(this).classed('drop', false); + }) + .call(nfDraggable.activate) + .call(nfConnectable.activate); + + return processGroup; + }; + + // attempt of space between component count and icon for process group contents + var CONTENTS_SPACER = 10; + var CONTENTS_VALUE_SPACER = 5; + + /** + * Updates the process groups in the specified selection. + * + * @param {selection} updated The process groups to be updated + */ + var updateProcessGroups = function (updated) { + if (updated.empty()) { + return; + } + + // process group border authorization + updated.select('rect.border') + .classed('unauthorized', function (d) { + return d.permissions.canRead === false; + }); + + // process group body authorization + updated.select('rect.body') + .classed('unauthorized', function (d) { + return d.permissions.canRead === false; + }); + + updated.each(function (processGroupData) { + var processGroup = d3.select(this); + var details = processGroup.select('g.process-group-details'); + + // update the component behavior as appropriate + nfCanvasUtils.editable(processGroup, nfConnectable, nfDraggable); + + // if this processor is visible, render everything + if (processGroup.classed('visible')) { + if (details.empty()) { + details = processGroup.append('g').attr('class', 'process-group-details'); + + // ------------------- + // contents background + // ------------------- + + details.append('rect') + .attrs({ + 'x': 0, + 'y': 32, + 'width': function () { + return processGroupData.dimensions.width + }, + 'height': 24, + 'fill': '#e3e8eb' + }); + + details.append('rect') + .attrs({ + 'x': 0, + 'y': function () { + return processGroupData.dimensions.height - 24; + }, + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 24, + 'fill': '#e3e8eb' + }); + + // -------- + // contents + // -------- + + // transmitting icon +// details.append('text') +// .attrs({ +// 'x': 10, +// 'y': 49, +// 'class': 'process-group-transmitting process-group-contents-icon', +// 'font-family': 'FontAwesome' +// }) +// .text('\uf140') +// .append("title") +// .text("Transmitting Remote Process Groups"); + + + // transmitting count +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-transmitting-count process-group-contents-count' +// }); + + // not transmitting icon +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-not-transmitting process-group-contents-icon', +// 'font-family': 'flowfont' +// }) +// .text('\ue80a') +// .append("title") +// .text("Not Transmitting Remote Process Groups"); + + // not transmitting count +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-not-transmitting-count process-group-contents-count' +// }); + + // running icon +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-running process-group-contents-icon', +// 'font-family': 'FontAwesome' +// }) +// .text('\uf04b') +// .append("title") +// .text("Running Components"); + + // running count +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-running-count process-group-contents-count' +// }); + + // stopped icon +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-stopped process-group-contents-icon', +// 'font-family': 'FontAwesome' +// }) +// .text('\uf04d') +// .append("title") +// .text("Stopped Components"); + + // stopped count +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-stopped-count process-group-contents-count' +// }); + + // invalid icon +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-invalid process-group-contents-icon', +// 'font-family': 'FontAwesome' +// }) +// .text('\uf071') +// .append("title") +// .text("Invalid Components"); + + // invalid count +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-invalid-count process-group-contents-count' +// }); + + // disabled icon +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-disabled process-group-contents-icon', +// 'font-family': 'flowfont' +// }) +// .text('\ue802') +// .append("title") +// .text("Disabled Components"); + + // disabled count +// details.append('text') +// .attrs({ +// 'y': 49, +// 'class': 'process-group-disabled-count process-group-contents-count' +// }); + + // up to date icon + details.append('text') + .attrs({ + 'x': 10, + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-up-to-date process-group-contents-icon', + 'font-family': 'FontAwesome' + }) + .text('\uf00c') + .append("title") + .text("Up to date Versioned Process Groups"); + + // up to date count + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-up-to-date-count process-group-contents-count' + }); + + // locally modified icon + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-locally-modified process-group-contents-icon', + 'font-family': 'FontAwesome' + }) + .text('\uf069') + .append("title") + .text("Locally modified Versioned Process Groups"); + + // locally modified count + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-locally-modified-count process-group-contents-count' + }); + + // stale icon + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-stale process-group-contents-icon', + 'font-family': 'FontAwesome' + }) + .text('\uf0aa') + .append("title") + .text("Stale Versioned Process Groups"); + + // stale count + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-stale-count process-group-contents-count' + }); + + // locally modified and stale icon + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-locally-modified-and-stale process-group-contents-icon', + 'font-family': 'FontAwesome' + }) + .text('\uf06a') + .append("title") + .text("Locally modified and stale Versioned Process Groups"); + + // locally modified and stale count + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-locally-modified-and-stale-count process-group-contents-count' + }); + + // sync failure icon + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-sync-failure process-group-contents-icon', + 'font-family': 'FontAwesome' + }) + .text('\uf128') + .append("title") + .text("Sync failure Versioned Process Groups"); + + // sync failure count + details.append('text') + .attrs({ + 'y': function () { + return processGroupData.dimensions.height - 7; + }, + 'class': 'process-group-sync-failure-count process-group-contents-count' + }); + + // ---------------- + // stats background + // ---------------- + + // queued + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 19, + 'x': 0, + 'y': 66, + 'fill': '#f4f6f7' + }); + + // border + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 1, + 'x': 0, + 'y': 84, + 'fill': '#c7d2d7' + }); + + // in + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 19, + 'x': 0, + 'y': 85, + 'fill': '#ffffff' + }); + + // border + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 1, + 'x': 0, + 'y': 103, + 'fill': '#c7d2d7' + }); + + // read/write + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 19, + 'x': 0, + 'y': 104, + 'fill': '#f4f6f7' + }); + + // border + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 1, + 'x': 0, + 'y': 122, + 'fill': '#c7d2d7' + }); + + // out + details.append('rect') + .attrs({ + 'width': function () { + return processGroupData.dimensions.width; + }, + 'height': 19, + 'x': 0, + 'y': 123, + 'fill': '#ffffff' + }); + + // ----- + // stats + // ----- + + // stats label container + var processGroupStatsLabel = details.append('g') + .attrs({ + 'transform': 'translate(6, 75)' + }); + + // queued label + processGroupStatsLabel.append('text') + .attrs({ + 'width': 73, + 'height': 10, + 'x': 4, + 'y': 5, + 'class': 'stats-label' + }) + .text('Queued'); + + // in label + processGroupStatsLabel.append('text') + .attrs({ + 'width': 73, + 'height': 10, + 'x': 4, + 'y': 24, + 'class': 'stats-label' + }) + .text('In'); + + // read/write label + processGroupStatsLabel.append('text') + .attrs({ + 'width': 73, + 'height': 10, + 'x': 4, + 'y': 42, + 'class': 'stats-label' + }) + .text('Read/Write'); + + // out label + processGroupStatsLabel.append('text') + .attrs({ + 'width': 73, + 'height': 10, + 'x': 4, + 'y': 60, + 'class': 'stats-label' + }) + .text('Out'); + + // stats value container + var processGroupStatsValue = details.append('g') + .attrs({ + 'transform': 'translate(95, 75)' + }); + + // queued value + var queuedText = processGroupStatsValue.append('text') + .attrs({ + 'width': 180, + 'height': 10, + 'x': 4, + 'y': 5, + 'class': 'process-group-queued stats-value' + }); + + // queued count + queuedText.append('tspan') + .attrs({ + 'class': 'count' + }); + + // queued size + queuedText.append('tspan') + .attrs({ + 'class': 'size' + }); + + // in value + var inText = processGroupStatsValue.append('text') + .attrs({ + 'width': 180, + 'height': 10, + 'x': 4, + 'y': 24, + 'class': 'process-group-in stats-value' + }); + + // in count + inText.append('tspan') + .attrs({ + 'class': 'count' + }); + + // in size + inText.append('tspan') + .attrs({ + 'class': 'size' + }); + + // in + inText.append('tspan') + .attrs({ + 'class': 'ports' + }); + + // read/write value + processGroupStatsValue.append('text') + .attrs({ + 'width': 180, + 'height': 10, + 'x': 4, + 'y': 42, + 'class': 'process-group-read-write stats-value' + }); + + // out value + var outText = processGroupStatsValue.append('text') + .attrs({ + 'width': 180, + 'height': 10, + 'x': 4, + 'y': 60, + 'class': 'process-group-out stats-value' + }); + + // out ports + outText.append('tspan') + .attrs({ + 'class': 'ports' + }); + + // out count + outText.append('tspan') + .attrs({ + 'class': 'count' + }); + + // out size + outText.append('tspan') + .attrs({ + 'class': 'size' + }); + + // stats value container + var processGroupStatsInfo = details.append('g') + .attrs({ + 'transform': 'translate(335, 75)' + }); + + // in info + processGroupStatsInfo.append('text') + .attrs({ + 'width': 25, + 'height': 10, + 'x': 4, + 'y': 24, + 'class': 'stats-info' + }) + .text('5 min'); + + // read/write info + processGroupStatsInfo.append('text') + .attrs({ + 'width': 25, + 'height': 10, + 'x': 4, + 'y': 42, + 'class': 'stats-info' + }) + .text('5 min'); + + // out info + processGroupStatsInfo.append('text') + .attrs({ + 'width': 25, + 'height': 10, + 'x': 4, + 'y': 60, + 'class': 'stats-info' + }) + .text('5 min'); + + // -------- + // comments + // -------- + + details.append('path') + .attrs({ + 'class': 'component-comments', + 'transform': 'translate(' + (processGroupData.dimensions.width - 2) + ', ' + (processGroupData.dimensions.height - 10) + ')', + 'd': 'm0,0 l0,8 l-8,0 z' + }); + + // ------------------- + // active thread count + // ------------------- + + // active thread count + details.append('text') + .attrs({ + 'class': 'active-thread-count-icon', + 'y': 20 + }) + .text('\ue83f'); + + // active thread icon + details.append('text') + .attrs({ + 'class': 'active-thread-count', + 'y': 20 + }); + + // --------- + // bulletins + // --------- + + // bulletin background + details.append('rect') + .attrs({ + 'class': 'bulletin-background', + 'x': function () { + return processGroupData.dimensions.width - 24; + }, + 'y': 32, + 'width': 24, + 'height': 24 + }); + + // bulletin icon + details.append('text') + .attrs({ + 'class': 'bulletin-icon', + 'x': function () { + return processGroupData.dimensions.width - 17; + }, + 'y': 49 + }) + .text('\uf24a'); + } + + // update transmitting + var transmitting = details.select('text.process-group-transmitting') + .classed('transmitting', function (d) { + return d.permissions.canRead && d.activeRemotePortCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.activeRemotePortCount === 0; + }); + var transmittingCount = details.select('text.process-group-transmitting-count') + .attr('x', function () { + var transmittingCountX = parseInt(transmitting.attr('x'), 10); + return transmittingCountX + Math.round(transmitting.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.activeRemotePortCount; + }); + transmittingCount.append("title").text("Transmitting Remote Process Groups"); + + // update not transmitting + var notTransmitting = details.select('text.process-group-not-transmitting') + .classed('not-transmitting', function (d) { + return d.permissions.canRead && d.inactiveRemotePortCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.inactiveRemotePortCount === 0; + }) + .attr('x', function () { + var transmittingX = parseInt(transmittingCount.attr('x'), 10); + return transmittingX + Math.round(transmittingCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var notTransmittingCount = details.select('text.process-group-not-transmitting-count') + .attr('x', function () { + var notTransmittingCountX = parseInt(notTransmitting.attr('x'), 10); + return notTransmittingCountX + Math.round(notTransmitting.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.inactiveRemotePortCount; + }); + notTransmittingCount.append("title").text("Not transmitting Remote Process Groups") + + // update running + var running = details.select('text.process-group-running') + .classed('running', function (d) { + return d.permissions.canRead && d.component.runningCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.runningCount === 0; + }) + .attr('x', function () { + var notTransmittingX = parseInt(notTransmittingCount.attr('x'), 10); + return notTransmittingX + Math.round(notTransmittingCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var runningCount = details.select('text.process-group-running-count') + .attr('x', function () { + var runningCountX = parseInt(running.attr('x'), 10); + return runningCountX + Math.round(running.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.runningCount; + }); + runningCount.append("title").text("Running Components"); + + // update stopped + var stopped = details.select('text.process-group-stopped') + .classed('stopped', function (d) { + return d.permissions.canRead && d.component.stoppedCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.stoppedCount === 0; + }) + .attr('x', function () { + var runningX = parseInt(runningCount.attr('x'), 10); + return runningX + Math.round(runningCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var stoppedCount = details.select('text.process-group-stopped-count') + .attr('x', function () { + var stoppedCountX = parseInt(stopped.attr('x'), 10); + return stoppedCountX + Math.round(stopped.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.stoppedCount; + }); + stoppedCount.append("title").text("Stopped Components"); + + // update invalid + var invalid = details.select('text.process-group-invalid') + .classed('invalid', function (d) { + return d.permissions.canRead && d.component.invalidCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.invalidCount === 0; + }) + .attr('x', function () { + var stoppedX = parseInt(stoppedCount.attr('x'), 10); + return stoppedX + Math.round(stoppedCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var invalidCount = details.select('text.process-group-invalid-count') + .attr('x', function () { + var invalidCountX = parseInt(invalid.attr('x'), 10); + return invalidCountX + Math.round(invalid.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.invalidCount; + }); + invalidCount.append("title").text("Invalid Components"); + + // update disabled + var disabled = details.select('text.process-group-disabled') + .classed('disabled', function (d) { + return d.permissions.canRead && d.component.disabledCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.disabledCount === 0; + }) + .attr('x', function () { + var invalidX = parseInt(invalidCount.attr('x'), 10); + return invalidX + Math.round(invalidCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var disabledCount = details.select('text.process-group-disabled-count') + .attr('x', function () { + var disabledCountX = parseInt(disabled.attr('x'), 10); + return disabledCountX + Math.round(disabled.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.disabledCount; + }); + disabledCount.append("title").text("Disabled Components"); + + // up to date current + var upToDate = details.select('text.process-group-up-to-date') + .classed('up-to-date', function (d) { + return d.permissions.canRead && d.component.upToDateCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.upToDateCount === 0; + }); + var upToDateCount = details.select('text.process-group-up-to-date-count') + .attr('x', function () { + var updateToDateCountX = parseInt(upToDate.attr('x'), 10); + return updateToDateCountX + Math.round(upToDate.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.upToDateCount; + }); + upToDateCount.append("title").text("Up to date Versioned Process Groups"); + + // update locally modified + var locallyModified = details.select('text.process-group-locally-modified') + .classed('locally-modified', function (d) { + return d.permissions.canRead && d.component.locallyModifiedCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.locallyModifiedCount === 0; + }) + .attr('x', function () { + var upToDateX = parseInt(upToDateCount.attr('x'), 10); + return upToDateX + Math.round(upToDateCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var locallyModifiedCount = details.select('text.process-group-locally-modified-count') + .attr('x', function () { + var locallyModifiedCountX = parseInt(locallyModified.attr('x'), 10); + return locallyModifiedCountX + Math.round(locallyModified.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.locallyModifiedCount; + }); + locallyModifiedCount.append("title").text("Locally modified Versioned Process Groups"); + + // update stale + var stale = details.select('text.process-group-stale') + .classed('stale', function (d) { + return d.permissions.canRead && d.component.staleCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.staleCount === 0; + }) + .attr('x', function () { + var locallyModifiedX = parseInt(locallyModifiedCount.attr('x'), 10); + return locallyModifiedX + Math.round(locallyModifiedCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var staleCount = details.select('text.process-group-stale-count') + .attr('x', function () { + var staleCountX = parseInt(stale.attr('x'), 10); + return staleCountX + Math.round(stale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.staleCount; + }); + staleCount.append("title").text("Stale Versioned Process Groups"); + + // update locally modified and stale + var locallyModifiedAndStale = details.select('text.process-group-locally-modified-and-stale') + .classed('locally-modified-and-stale', function (d) { + return d.permissions.canRead && d.component.locallyModifiedAndStaleCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.locallyModifiedAndStaleCount === 0; + }) + .attr('x', function () { + var staleX = parseInt(staleCount.attr('x'), 10); + return staleX + Math.round(staleCount.node().getComputedTextLength()) + CONTENTS_SPACER; + }); + var locallyModifiedAndStaleCount = details.select('text.process-group-locally-modified-and-stale-count') + .attr('x', function () { + var locallyModifiedAndStaleCountX = parseInt(locallyModifiedAndStale.attr('x'), 10); + return locallyModifiedAndStaleCountX + Math.round(locallyModifiedAndStale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.locallyModifiedAndStaleCount; + }); + locallyModifiedAndStaleCount.append("title").text("Locally modified and stale Versioned Process Groups"); + + // update sync failure + var syncFailure = details.select('text.process-group-sync-failure') + .classed('sync-failure', function (d) { + return d.permissions.canRead && d.component.syncFailureCount > 0; + }) + .classed('zero', function (d) { + return d.permissions.canRead && d.component.syncFailureCount === 0; + }) + .attr('x', function () { + var syncFailureX = parseInt(locallyModifiedAndStaleCount.attr('x'), 10); + return syncFailureX + Math.round(locallyModifiedAndStaleCount.node().getComputedTextLength()) + CONTENTS_SPACER - 2; + }); + var syncFailureCount = details.select('text.process-group-sync-failure-count') + .attr('x', function () { + var syncFailureCountX = parseInt(syncFailure.attr('x'), 10); + return syncFailureCountX + Math.round(syncFailure.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + }) + .text(function (d) { + return d.syncFailureCount; + }); + syncFailureCount.append("title").text("Sync failure Versioned Process Groups"); + + /** + * update version control information + * @author: Renu + * @desc for lines 1110-1201: based on state of the process group, environment selection and submit button enable/disable + */ + var versionControl = processGroup.select('text.version-control') + .styles({ + 'visibility': isUnderVersionControl(processGroupData) ? 'visible' : 'hidden', + 'fill': function () { + if (isUnderVersionControl(processGroupData)) { + var vciState = processGroupData.versionedFlowState; + if (vciState === 'SYNC_FAILURE') { + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + + return '#666666'; + } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') { + console.log("locally but stale in style"); + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + + return '#BA554A'; + } else if (vciState === 'STALE') { + console.log("stale in style"); + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + return '#BA554A'; + } else if (vciState === 'LOCALLY_MODIFIED') { + console.log("locally modified in style"); + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + + return '#666666'; + } else { + return '#1A9964'; + $('#environmentType').prop('disabled', false); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + } + } else { + $('#environmentType').prop('disabled', true); + return '#000'; + } + } + }) + .text(function () { + if (isUnderVersionControl(processGroupData)) { + var vciState = processGroupData.versionedFlowState; + if (vciState === 'SYNC_FAILURE') { + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + + return '\uf128' + } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') { + console.log("locally but stale in text"); + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + + return '\uf06a'; + } else if (vciState === 'STALE') { + console.log("stale in text"); + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + return '\uf0aa'; + } else if (vciState === 'LOCALLY_MODIFIED') { + console.log("locally modified in text"); + $('#environmentType').prop('disabled', true); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + return '\uf069'; + } else { + return '\uf00c'; + $('#environmentType').prop('disabled', false); + if($('#environmentType').val() && !$('#environmentType').prop('disabled')){ + $('#operate-submit-btn').prop('disabled', false); + }else{$('#operate-submit-btn').prop('disabled', true);} + } + } else { + $('#environmentType').prop('disabled', true); + return ''; + } + }); + + if (processGroupData.permissions.canRead) { + // version control tooltip + versionControl.each(function () { + // get the tip + var tip = d3.select('#version-control-tip-' + processGroupData.id); + + // if there are validation errors generate a tooltip + if (isUnderVersionControl(processGroupData)) { + // create the tip if necessary + if (tip.empty()) { + tip = d3.select('#process-group-tooltips').append('div') + .attr('id', function () { + return 'version-control-tip-' + processGroupData.id; + }) + .attr('class', 'tooltip nifi-tooltip'); + } + + // update the tip + tip.html(function () { + var vci = processGroupData.component.versionControlInformation; + var versionControlTip = $('
').text('Tracking to "' + vci.flowName + '" version ' + vci.version + ' in "' + vci.registryName + ' - ' + vci.bucketName + '"'); + var versionControlStateTip = $('
').text(nfCommon.getVersionControlTooltip(vci)); + return $('
').append(versionControlTip).append('
').append(versionControlStateTip).html(); + }); + + // add the tooltip + nfCanvasUtils.canvasTooltip(tip, d3.select(this)); + } else { + // remove the tip if necessary + if (!tip.empty()) { + tip.remove(); + } + } + }); + + // update the process group comments + processGroup.select('path.component-comments') + .style('visibility', nfCommon.isBlank(processGroupData.component.comments) ? 'hidden' : 'visible') + .each(function () { + // get the tip + var tip = d3.select('#comments-tip-' + processGroupData.id); + + // if there are validation errors generate a tooltip + if (nfCommon.isBlank(processGroupData.component.comments)) { + // remove the tip if necessary + if (!tip.empty()) { + tip.remove(); + } + } else { + // create the tip if necessary + if (tip.empty()) { + tip = d3.select('#process-group-tooltips').append('div') + .attr('id', function () { + return 'comments-tip-' + processGroupData.id; + }) + .attr('class', 'tooltip nifi-tooltip'); + } + + // update the tip + tip.text(processGroupData.component.comments); + + // add the tooltip + nfCanvasUtils.canvasTooltip(tip, d3.select(this)); + } + }); + + // update the process group name + processGroup.select('text.process-group-name') + .attrs({ + 'x': function () { + if (isUnderVersionControl(processGroupData)) { + var versionControlX = parseInt(versionControl.attr('x'), 10); + return versionControlX + Math.round(versionControl.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER; + } else { + return 10; + } + }, + 'width': function () { + if (isUnderVersionControl(processGroupData)) { + var versionControlX = parseInt(versionControl.attr('x'), 10); + var processGroupNameX = parseInt(d3.select(this).attr('x'), 10); + return 300 - (processGroupNameX - versionControlX); + } else { + return 300; + } + } + }) + .each(function (d) { + var processGroupName = d3.select(this); + + // reset the process group name to handle any previous state + processGroupName.text(null).selectAll('title').remove(); + + // apply ellipsis to the process group name as necessary + nfCanvasUtils.ellipsis(processGroupName, d.component.name); + }) + .append('title') + .text(function (d) { + return d.component.name; + }); + } else { + // clear the process group comments + processGroup.select('path.component-comments').style('visibility', 'hidden'); + + // clear the process group name + processGroup.select('text.process-group-name') + .attrs({ + 'x': 10, + 'width': 316 + }) + .text(null); + + // clear tooltips + processGroup.call(removeTooltips); + } + + // populate the stats + processGroup.call(updateProcessGroupStatus); + } else { + if (processGroupData.permissions.canRead) { + // update the process group name + processGroup.select('text.process-group-name') + .text(function (d) { + var name = d.component.name; + if (name.length > PREVIEW_NAME_LENGTH) { + return name.substring(0, PREVIEW_NAME_LENGTH) + String.fromCharCode(8230); + } else { + return name; + } + }); + } else { + // clear the process group name + processGroup.select('text.process-group-name').text(null); + } + + // remove the tooltips + processGroup.call(removeTooltips); + + // remove the details if necessary + if (!details.empty()) { + details.remove(); + } + } + }); + }; + + /** + * Updates the process group status. + * + * @param {selection} updated The process groups to be updated + */ + var updateProcessGroupStatus = function (updated) { + if (updated.empty()) { + return; + } + + // queued count value + updated.select('text.process-group-queued tspan.count') + .text(function (d) { + return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.queued, ' '); + }); + + // queued size value + updated.select('text.process-group-queued tspan.size') + .text(function (d) { + return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.queued, ' '); + }); + + // in count value + updated.select('text.process-group-in tspan.count') + .text(function (d) { + return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.input, ' '); + }); + + // in size value + updated.select('text.process-group-in tspan.size') + .text(function (d) { + return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.input, ' '); + }); + + // in ports value + updated.select('text.process-group-in tspan.ports') + .text(function (d) { + return ' ' + String.fromCharCode(8594) + ' ' + d.inputPortCount; + }); + + // read/write value + updated.select('text.process-group-read-write') + .text(function (d) { + return d.status.aggregateSnapshot.read + ' / ' + d.status.aggregateSnapshot.written; + }); + + // out ports value + updated.select('text.process-group-out tspan.ports') + .text(function (d) { + return d.outputPortCount + ' ' + String.fromCharCode(8594) + ' '; + }); + + // out count value + updated.select('text.process-group-out tspan.count') + .text(function (d) { + return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' '); + }); + + // out size value + updated.select('text.process-group-out tspan.size') + .text(function (d) { + return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.output, ' '); + }); + + updated.each(function (d) { + var processGroup = d3.select(this); + var offset = 0; + + // ------------------- + // active thread count + // ------------------- + + nfCanvasUtils.activeThreadCount(processGroup, d, function (off) { + offset = off; + }); + + // --------- + // bulletins + // --------- + + processGroup.select('rect.bulletin-background').classed('has-bulletins', function () { + return !nfCommon.isEmpty(d.status.aggregateSnapshot.bulletins); + }); + + nfCanvasUtils.bulletins(processGroup, d, function () { + return d3.select('#process-group-tooltips'); + }, offset); + }); + }; + + /** + * Removes the process groups in the specified selection. + * + * @param {selection} removed The process groups to be removed + */ + var removeProcessGroups = function (removed) { + if (removed.empty()) { + return; + } + + removed.call(removeTooltips).remove(); + }; + + /** + * Removes the tooltips for the process groups in the specified selection. + * + * @param {selection} removed + */ + var removeTooltips = function (removed) { + removed.each(function (d) { + // remove any associated tooltips + $('#bulletin-tip-' + d.id).remove(); + $('#version-control-tip-' + d.id).remove(); + $('#comments-tip-' + d.id).remove(); + }); + }; + + var nfProcessGroup = { + /** + * Initializes of the Process Group handler. + * + * @param nfConnectableRef The nfConnectable module. + * @param nfDraggableRef The nfDraggable module. + * @param nfSelectableRef The nfSelectable module. + * @param nfContextMenuRef The nfContextMenu module. + */ + init: function (nfConnectableRef, nfDraggableRef, nfSelectableRef, nfContextMenuRef) { + nfConnectable = nfConnectableRef; + nfDraggable = nfDraggableRef; + nfSelectable = nfSelectableRef; + nfContextMenu = nfContextMenuRef; + + processGroupMap = d3.map(); + removedCache = d3.map(); + addedCache = d3.map(); + + // create the process group container + processGroupContainer = d3.select('#canvas').append('g') + .attrs({ + 'pointer-events': 'all', + 'class': 'process-groups' + }); + }, + + /** + * Adds the specified process group entity. + * + * @param processGroupEntities The process group + * @param options Configuration options + */ + add: function (processGroupEntities, options) { + var selectAll = false; + if (nfCommon.isDefinedAndNotNull(options)) { + selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll; + } + + // get the current time + var now = new Date().getTime(); + + var add = function (processGroupEntity) { + addedCache.set(processGroupEntity.id, now); + + // add the process group + processGroupMap.set(processGroupEntity.id, $.extend({ + type: 'ProcessGroup', + dimensions: dimensions + }, processGroupEntity)); + }; + + // determine how to handle the specified process groups + if ($.isArray(processGroupEntities)) { + $.each(processGroupEntities, function (_, processGroupEntity) { + add(processGroupEntity); + }); + } else if (nfCommon.isDefinedAndNotNull(processGroupEntities)) { + add(processGroupEntities); + } + + // select + var selection = select(); + + // enter + var entered = renderProcessGroups(selection.enter(), selectAll); + + // update + updateProcessGroups(selection.merge(entered)); + }, + + /** + * Populates the graph with the specified process groups. + * + * @argument {object | array} processGroupEntities The process groups to add + * @argument {object} options Configuration options + */ + set: function (processGroupEntities, options) { + var selectAll = false; + var transition = false; + var overrideRevisionCheck = false; + if (nfCommon.isDefinedAndNotNull(options)) { + selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll; + transition = nfCommon.isDefinedAndNotNull(options.transition) ? options.transition : transition; + overrideRevisionCheck = nfCommon.isDefinedAndNotNull(options.overrideRevisionCheck) ? options.overrideRevisionCheck : overrideRevisionCheck; + } + + var set = function (proposedProcessGroupEntity) { + var currentProcessGroupEntity = processGroupMap.get(proposedProcessGroupEntity.id); + + // set the process group if appropriate due to revision and wasn't previously removed + if ((nfClient.isNewerRevision(currentProcessGroupEntity, proposedProcessGroupEntity) && !removedCache.has(proposedProcessGroupEntity.id)) || overrideRevisionCheck === true) { + processGroupMap.set(proposedProcessGroupEntity.id, $.extend({ + type: 'ProcessGroup', + dimensions: dimensions + }, proposedProcessGroupEntity)); + } + }; + + // determine how to handle the specified process groups + if ($.isArray(processGroupEntities)) { + $.each(processGroupMap.keys(), function (_, key) { + var currentProcessGroupEntity = processGroupMap.get(key); + var isPresent = $.grep(processGroupEntities, function (proposedProcessGroupEntity) { + return proposedProcessGroupEntity.id === currentProcessGroupEntity.id; + }); + + // if the current process group is not present and was not recently added, remove it + if (isPresent.length === 0 && !addedCache.has(key)) { + processGroupMap.remove(key); + } + }); + $.each(processGroupEntities, function (_, processGroupEntity) { + set(processGroupEntity); + }); + } else if (nfCommon.isDefinedAndNotNull(processGroupEntities)) { + set(processGroupEntities); + } + + // select + var selection = select(); + + // enter + var entered = renderProcessGroups(selection.enter(), selectAll); + + // update + var updated = selection.merge(entered); + updated.call(updateProcessGroups).call(nfCanvasUtils.position, transition); + + // exit + selection.exit().call(removeProcessGroups); + }, + + /** + * If the process group id is specified it is returned. If no process group id + * specified, all process groups are returned. + * + * @param {string} id + */ + get: function (id) { + if (nfCommon.isUndefined(id)) { + return processGroupMap.values(); + } else { + return processGroupMap.get(id); + } + }, + + /** + * If the process group id is specified it is refresh according to the current + * state. If no process group id is specified, all process groups are refreshed. + * + * @param {string} id Optional + */ + refresh: function (id) { + if (nfCommon.isDefinedAndNotNull(id)) { + d3.select('#id-' + id).call(updateProcessGroups); + } else { + d3.selectAll('g.process-group').call(updateProcessGroups); + } + }, + + /** + * Refreshes the components necessary after a pan event. + */ + pan: function () { + d3.selectAll('g.process-group.entering, g.process-group.leaving').call(updateProcessGroups); + }, + + /** + * Reloads the process group state from the server and refreshes the UI. + * If the process group is currently unknown, this function reloads the canvas. + * + * @param {string} id The process group id + */ + reload: function (id) { + if (processGroupMap.has(id)) { + var processGroupEntity = processGroupMap.get(id); + return $.ajax({ + type: 'GET', + url: processGroupEntity.uri, + dataType: 'json' + }).done(function (response) { + nfProcessGroup.set(response); + }); + } + }, + + /** + * Positions the component. + * + * @param {string} id The id + */ + position: function (id) { + d3.select('#id-' + id).call(nfCanvasUtils.position); + }, + + /** + * Removes the specified process group. + * + * @param {string} processGroupIds The process group id(s) + */ + remove: function (processGroupIds) { + var now = new Date().getTime(); + + if ($.isArray(processGroupIds)) { + $.each(processGroupIds, function (_, processGroupId) { + removedCache.set(processGroupId, now); + processGroupMap.remove(processGroupId); + }); + } else { + removedCache.set(processGroupIds, now); + processGroupMap.remove(processGroupIds); + } + + // apply the selection and handle all removed process groups + select().exit().call(removeProcessGroups); + }, + + /** + * Removes all process groups. + */ + removeAll: function () { + nfProcessGroup.remove(processGroupMap.keys()); + }, + + /** + * Expires the caches up to the specified timestamp. + * + * @param timestamp + */ + expireCaches: function (timestamp) { + var expire = function (cache) { + cache.each(function (entryTimestamp, id) { + if (timestamp > entryTimestamp) { + cache.remove(id); + } + }); + }; + + expire(addedCache); + expire(removedCache); + }, + + /** + * Enters the specified group. + * + * @param {string} groupId + */ + enterGroup: function (groupId) { + + // hide the context menu + nfContextMenu.hide(); + + // set the new group id + nfCanvasUtils.setGroupId(groupId); + + // reload the graph + return nfCanvasUtils.reload().done(function () { + + // attempt to restore the view + var viewRestored = nfCanvasUtils.restoreUserView(); + + // if the view was not restore attempt to fit + if (viewRestored === false) { + nfCanvasUtils.fitCanvas(); + } + + // update URL deep linking params + nfCanvasUtils.setURLParameters(groupId, d3.select()); + + }).fail(function () { + nfDialog.showOkDialog({ + headerText: 'Process Group', + dialogContent: 'Unable to enter the selected group.' + }); + }); + } + }; + + return nfProcessGroup; +})); -- cgit 1.2.3-korg