sardell commented on code in PR #8273: URL: https://github.com/apache/nifi/pull/8273#discussion_r1474564178
########## nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js: ########## @@ -400,6 +406,554 @@ } } + /** + * The flow analysis controller. + */ + + this.flowAnalysis = { + + /** + * Create the list of rule violations + */ + buildRuleViolationsList: function(rules, violationsAndRecs) { + var ruleViolationCountEl = $('#rule-violation-count'); + var ruleViolationListEl = $('#rule-violations-list'); + var ruleWarningCountEl = $('#rule-warning-count'); + var ruleWarningListEl = $('#rule-warnings-list'); + var violations = violationsAndRecs.filter(function (violation) { + return violation.enforcementPolicy === 'ENFORCE' + }); + var warnings = violationsAndRecs.filter(function (violation) { + return violation.enforcementPolicy === 'WARN' + }); + ruleViolationCountEl.empty().text('(' + violations.length + ')'); + ruleWarningCountEl.empty().text('(' + warnings.length + ')'); + ruleViolationListEl.empty(); + violations.forEach(function(violation) { + var rule = rules.find(function(rule) { + return rule.id === violation.ruleId; + }); + // create DOM elements + var violationListItemEl = $('<li></li>'); + var violationEl = $('<div class="violation-list-item"></div>'); + var violationListItemWrapperEl = $('<div class="violation-list-item-wrapper"></div>'); + var violationRuleEl = $('<div class="rule-violations-list-item-name"></div>'); + var violationListItemNameEl = $('<div class="violation-list-item-name"></div>'); + var violationListItemIdEl = $('<span class="violation-list-item-id"></span>'); + var violationInfoButtonEl = $('<button class="violation-menu-btn"><i class="fa fa-ellipsis-v rules-list-item-menu-target" aria-hidden="true"></i></button>'); + + // add text content and button data + $(violationRuleEl).text(rule.name); + $(violationListItemNameEl).text(violation.subjectDisplayName); + $(violationListItemIdEl).text(violation.subjectId); + $(violationListItemEl).append(violationRuleEl).append(violationListItemWrapperEl); + $(violationInfoButtonEl).data('violationInfo', violation); + + // build list DOM structure + violationListItemWrapperEl.append(violationListItemNameEl).append(violationListItemIdEl); + violationEl.append(violationListItemWrapperEl).append(violationInfoButtonEl); + violationListItemEl.append(violationRuleEl).append(violationEl) + ruleViolationListEl.append(violationListItemEl); + }); + + warnings.forEach(function(warning) { + var rule = rules.find(function(rule) { + return rule.id === warning.ruleId; + }); + // create DOM elements + var warningListItemEl = $('<li></li>'); + var warningEl = $('<div class="warning-list-item"></div>'); + var warningListItemWrapperEl = $('<div class="warning-list-item-wrapper"></div>'); + var warningRuleEl = $('<div class="rule-warnings-list-item-name"></div>'); + var warningListItemNameEl = $('<div class="warning-list-item-name"></div>'); + var warningListItemIdEl = $('<span class="warning-list-item-id"></span>'); + var warningInfoButtonEl = $('<button class="violation-menu-btn"><i class="fa fa-ellipsis-v rules-list-item-menu-target" aria-hidden="true"></i></button>'); + + // add text content and button data + $(warningRuleEl).text(rule.name); + $(warningListItemNameEl).text(warning.subjectDisplayName); + $(warningListItemIdEl).text(warning.subjectId); + $(warningListItemEl).append(warningRuleEl).append(warningListItemWrapperEl); + $(warningInfoButtonEl).data('violationInfo', warning); + + // build list DOM structure + warningListItemWrapperEl.append(warningListItemNameEl).append(warningListItemIdEl); + warningEl.append(warningListItemWrapperEl).append(warningInfoButtonEl); + warningListItemEl.append(warningRuleEl).append(warningEl) + ruleWarningListEl.append(warningListItemEl); + }); + }, + + /** + * + * Render a new list when it differs from the previous violations response + */ + buildRuleViolations: function(rules, violations) { + if (Object.keys(previousRulesResponse).length !== 0) { + var previousRulesResponseSorted = _.sortBy(previousRulesResponse.ruleViolations, 'subjectId'); + var violationsSorted = _.sortBy(violations, 'subjectId'); + if (!_.isEqual(previousRulesResponseSorted, violationsSorted)) { + this.buildRuleViolationsList(rules, violations); + } + } else { + this.buildRuleViolationsList(rules, violations); + } + }, + + /** + * Create the list of flow policy rules + */ + buildRuleList: function(ruleType, violationsMap, rec) { + var requiredRulesListEl = $('#required-rules-list'); + var recommendedRulesListEl = $('#recommended-rules-list'); + var rule = $('<li class="rules-list-item"></li>').append($(rec.requirement).append(rec.requirementInfoButton)) + var violationsListEl = ''; + var violationCountEl = ''; + + var violations = violationsMap.get(rec.id); + if (!!violations) { + if (violations.length === 1) { + violationCountEl = '<div class="rule-' + ruleType + 's-count">' + violations.length + ' ' + ruleType + '</div>'; + } else { + violationCountEl = '<div class="rule-' + ruleType + 's-count">' + violations.length + ' ' + ruleType + 's</div>'; + } + violationsListEl = $('<ul class="rule-' + ruleType + 's-list"></ul>'); + violations.forEach(function(violation) { + // create DOM elements + var violationListItemEl = $('<li class="' + ruleType + '-list-item"></li>'); + var violationWrapperEl = $('<div class="' + ruleType + '-list-item-wrapper"></div>'); + var violationNameEl = $('<div class="' + ruleType + '-list-item-name"></div>'); + var violationIdEl = $('<span class="' + ruleType + '-list-item-id"></span>'); + var violationInfoButtonEl = $('<button class="violation-menu-btn"><i class="fa fa-ellipsis-v rules-list-item-menu-target" aria-hidden="true"></i></button>'); + + // add text content and button data + violationNameEl.text(violation.subjectDisplayName); + violationIdEl.text(violation.subjectId); + + // build list DOM structure + violationListItemEl.append(violationWrapperEl); + violationWrapperEl.append(violationNameEl).append(violationIdEl) + violationInfoButtonEl.data('violationInfo', violation); + (violationsListEl).append(violationListItemEl.append(violationInfoButtonEl)); + }); + rule.append(violationCountEl).append(violationsListEl); + } + ruleType === 'violation' ? requiredRulesListEl.append(rule) : recommendedRulesListEl.append(rule); + }, + + /** + * Loads the current status of the flow. + */ + loadFlowPolicies: function () { + var flowAnalysisCtrl = this; + var requiredRulesListEl = $('#required-rules-list'); + var recommendedRulesListEl = $('#recommended-rules-list'); + var requiredRuleCountEl = $('#required-rule-count'); + var recommendedRuleCountEl = $('#recommended-rule-count'); + + var groupId = nfCanvasUtils.getGroupId(); + if (groupId !== 'root') { + $.ajax({ + type: 'GET', + url: '../nifi-api/flow/flow-analysis/results/' + groupId, + dataType: 'json', + context: this + }).done(function (response) { + var recommendations = []; + var requirements = []; + var requirementsTotal = 0; + var recommendationsTotal = 0; + + if (!_.isEqual(previousRulesResponse, response)) { + // clear previous accordion content + requiredRulesListEl.empty(); + recommendedRulesListEl.empty(); + flowAnalysisCtrl.buildRuleViolations(response.rules, response.ruleViolations); + + // For each ruleViolations: + // * group violations by ruleId + // * build DOM elements + // * get the ruleId and find the matching rule id + // * append violation list to matching rule list item + var violationsMap = new Map(); + response.ruleViolations.forEach(function(violation) { + if (violationsMap.has(violation.ruleId)){ + violationsMap.get(violation.ruleId).push(violation); + } else { + violationsMap.set(violation.ruleId, [violation]); + } + }); + + // build list of recommendations + response.rules.forEach(function(rule) { + if (rule.enforcementPolicy === 'WARN') { + var requirement = '<div class="rules-list-rule-info"></div>'; + var requirementName = $('<div></div>').text(rule.name); + var requirementInfoButton = '<button class="rule-menu-btn"><i class="fa fa-ellipsis-v rules-list-item-menu-target" aria-hidden="true"></i></button>'; + recommendations.push( + { + 'requirement': $(requirement).append(requirementName), + 'requirementInfoButton': $(requirementInfoButton).data('ruleInfo', rule), + 'id': rule.id + } + ) + recommendationsTotal++; + } + }); + + // add class to notification icon for recommended rules + var hasRecommendations = response.ruleViolations.findIndex(function(violation) { + return violation.enforcementPolicy === 'WARN'; + }); + if (hasRecommendations !== -1) { + $('#flow-analysis .flow-analysis-notification-icon ').addClass('recommendations'); + } else { + $('#flow-analysis .flow-analysis-notification-icon ').removeClass('recommendations'); + } + + // build list of requirements + recommendedRuleCountEl.empty().append('(' + recommendationsTotal + ')'); + recommendations.forEach(function(rec) { + flowAnalysisCtrl.buildRuleList('recommendation', violationsMap, rec); + }); + + response.rules.forEach(function(rule) { + if (rule.enforcementPolicy === 'ENFORCE') { + var requirement = '<div class="rules-list-rule-info"></div>'; + var requirementName = $('<div></div>').text(rule.name); + var requirementInfoButton = '<button class="rule-menu-btn"><i class="fa fa-ellipsis-v rules-list-item-menu-target" aria-hidden="true"></i></button>'; + requirements.push( + { + 'requirement': $(requirement).append(requirementName), + 'requirementInfoButton': $(requirementInfoButton).data('ruleInfo', rule), + 'id': rule.id + } + ) + requirementsTotal++; + } + }); + + // add class to notification icon for required rules + var hasViolations = response.ruleViolations.findIndex(function(violation) { + return violation.enforcementPolicy === 'ENFORCE'; + }) + if (hasViolations !== -1) { + $('#flow-analysis .flow-analysis-notification-icon ').addClass('violations'); + } else { + $('#flow-analysis .flow-analysis-notification-icon ').removeClass('violations'); + } + + requiredRuleCountEl.empty().append('(' + requirementsTotal + ')'); + + // build violations + requirements.forEach(function(rec) { + flowAnalysisCtrl.buildRuleList('violation', violationsMap, rec); + }); + + $('#required-rules').accordion('refresh'); + $('#recommended-rules').accordion('refresh'); + // report the updated status + previousRulesResponse = response; + + // setup rule menu handling + flowAnalysisCtrl.setRuleMenuHandling(response); + + // setup violation menu handling + flowAnalysisCtrl.setViolationMenuHandling(response, groupId); + } + }).fail(nfErrorHandler.handleAjaxError); + } + }, + + /** + * Set event bindings for rule menus + */ + setRuleMenuHandling: function() { + $('.rule-menu-btn').click(function(event) { + // stop event from immediately bubbling up to document and triggering closeRuleWindow + event.stopPropagation(); + var ruleInfo = $(this).data('ruleInfo'); + $('#violation-menu').hide(); + $('#rule-menu').show(); + $('#rule-menu').position({ + my: "left top", + at: "left top", + of: event + }); + + // rule menu bindings + $('#rule-menu-more-info').on( "click", openRuleMoreInfoDialog); + $('#rule-menu-edit-rule').on('click', openRuleDetailsDialog); + $(document).on('click', closeRuleWindow); + + function closeRuleWindow(e) { + if ($(e.target).parents("#rule-menu").length === 0) { + $("#rule-menu").hide(); + unbindRuleMenuHandling(); + } + } + + function openRuleDetailsDialog() { + $('#rule-menu').hide(); + nfSettings.showSettings().done(function() { + nfSettings.selectFlowAnalysisRule(ruleInfo.id); + }); + unbindRuleMenuHandling() + } + + function openRuleMoreInfoDialog() { + $('#rule-menu').hide(); + $('#rule-type-pill').empty() + .removeClass() + .addClass(ruleInfo.enforcementPolicy.toLowerCase() + ' rule-type-pill') + .append(ruleInfo.enforcementPolicy); + $('#rule-display-name').empty().append(ruleInfo.name); + $('#rule-description').empty().append(ruleInfo.descriptors['component-type'].description); + $( "#rule-menu-more-info-dialog" ).modal( "show" ); + $('.rule-docs-link').click(function () { + // open the documentation for this flow analysis rule Review Comment: Please disregard my last comment. I was finally able to reproduce the issue (happens when clicking from one rule options menu trigger then immediately clicking on another with no breakpoint set). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@nifi.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org