From 4104cfd78d829ec4ab6bf4470ae472a6eda74171 Mon Sep 17 00:00:00 2001 From: Shane Ardell Date: Tue, 16 Apr 2024 17:34:19 -0400 Subject: [PATCH] NIFI-11520: Add a menu to display Flow Analysis report results (#8273) * NIFI-11520: init ui work for flow analysis UI * NIFI-11520: use .text() to render user input data * NIFI-11520: update urls for analysis requests * NIFI-11520: add WARN enforcement level to ui * NIFI-11520: ui bug fixes * fix rule bindings * use correct count for rule violations * NIFI-11520: move drawer markup into partial file * NIFI-11520: remove old recs and policies naming * NIFI-11520: comments and code cleanup * NIFI-11520: fix linting errors * NIFI-11520: restore refresh button logic * NIFI-11520: remove timer * NIFI-11520: add checkbox to only show warning violations * NIFI-11520: style and copy changes * change copy of violation checkboxes * show correct details in violation dialog * add overflow to violations menu * NIFI-11520: add missing license header * NIFI-11520: remove unused function * NIFI-11520: cleanup rule and violation menu handling * NIFI-11520: remove single use functions * NIFI-11520: change function name to match established pattern * NIFI-11520: rename function * NIFI-11520: remove rule and violation details menu * NIFI-11520: fix issue causing wrong documentation to be displayed * NIFI-11520: fix go to button for components in nested process groups * NIFI-11520: use refresh interval returned from the backend * NIFI-11520: reload analysis when canvas is refreshed * NIFI-11520: add violation details dialog with correct message * NIFI-11520: disabled go to component when root group is violation * NIFI-11520: remove unused CSS styles * NIFI-11520: addressing more feedback: * fix flow analysis drawer button styling * disable edit rule if user does not have read permission * fix broken warning list * add loader and disable check now button while report is running * NIFI-11520: handle violations without read permission * NIFI-11520: disable go to component if not processor * NIFI-11520: remove create analysis button and logic * NIFI-11520: add pending analysis message * NIFI-11520: protect against scenario where currentUser not loaded * NIFI-11520: determine root group based on groupId being null * NIFI-11520: address review feedback * simplify go to logic by only showing for processors * hide go to button instead of disabling * NIFI-11520: fix hidden state * NIFI-11520: hide go to based on permissions This closes #8273 --- .../nifi-web/nifi-web-ui/pom.xml | 2 + .../main/resources/filters/summary.properties | 1 + .../src/main/webapp/WEB-INF/pages/canvas.jsp | 2 + .../partials/canvas/flow-analysis-drawer.jsp | 84 +++ .../WEB-INF/partials/canvas/flow-status.jsp | 1 + .../canvas/violation-description-dialog.jsp | 28 + .../src/main/webapp/css/canvas.css | 1 + .../main/webapp/css/flow-analysis-drawer.css | 463 ++++++++++++++ .../nf-ng-canvas-flow-status-controller.js | 584 +++++++++++++++++- .../main/webapp/js/nf/canvas/nf-actions.js | 1 + .../js/nf/canvas/nf-canvas-bootstrap.js | 2 +- .../js/nf/canvas/nf-flow-analysis-rule.js | 10 +- .../src/main/webapp/js/nf/nf-common.js | 19 + 13 files changed, 1185 insertions(+), 13 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-analysis-drawer.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/violation-description-dialog.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-analysis-drawer.css 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 c401e4b440..f1d087c979 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 @@ -741,6 +741,7 @@ ${staging.dir}/css/settings.css ${staging.dir}/css/about.css ${staging.dir}/css/status-history.css + ${staging.dir}/css/flow-analysis-drawer.css @@ -780,6 +781,7 @@ ${staging.dir}/css/processor-details.css ${staging.dir}/css/connection-details.css ${staging.dir}/css/status-history.css + ${staging.dir}/css/flow-analysis-drawer.css ${staging.dir}/css/summary.css diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties index a45a07a430..9f3cc38b0d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties @@ -43,4 +43,5 @@ nf.summary.style.tags=\n\ \n\ \n\ +\n\ \ No newline at end of file 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 51e1e08160..152435b891 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 @@ -128,6 +128,7 @@
+
@@ -145,6 +146,7 @@ + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-analysis-drawer.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-analysis-drawer.jsp new file mode 100644 index 0000000000..ad1f64ab1f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-analysis-drawer.jsp @@ -0,0 +1,84 @@ +<%-- + 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" %> +
+
+
+
Rules analysis pending...
+
+
+
+
Flow Guide
+
+
+
+ Show enforced violations +
+
+
+ Show warning violations +
+
+
+
NiFi Flow
+
+
+ +
+
+
Enforced Rules
+
+
    +
+
+ + + +
+
+
Enforced Violations
+
+
    +
    + +
    +
    +
    Warning Violations
    +
    +
      +
      + +
      +
        +
      • View Documentation
      • +
      • Edit Rule
      • +
      +
      + +
      +
        +
      • Violation details
      • +
      • Go to component
      • +
      +
      +
      +
      \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-status.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-status.jsp index fbf33a9715..6f339a320e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-status.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flow-status.jsp @@ -71,6 +71,7 @@
      + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/violation-description-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/violation-description-dialog.jsp new file mode 100644 index 0000000000..23dc2d10e1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/violation-description-dialog.jsp @@ -0,0 +1,28 @@ +<%-- + 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" %> + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css index 8042c22a13..240dd14765 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css @@ -53,3 +53,4 @@ @import url(status-history.css); @import url(../fonts/flowfont/flowfont.css); @import url(../assets/font-awesome/css/font-awesome.css); +@import url(flow-analysis-drawer.css); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-analysis-drawer.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-analysis-drawer.css new file mode 100644 index 0000000000..50299bd982 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-analysis-drawer.css @@ -0,0 +1,463 @@ +/* + * 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. + */ + +.flow-analysis { + background-color: #E3E8EB; + padding: 0; +} + +.flow-analysis:hover, +.flow-analysis.opened { + background-color: #FFFFFF; +} + +#flow-status .flow-analysis .fa-lightbulb-o { + padding: 4px; + width: 14px; + height: 14px; + border-radius: 15px; +} + +#flow-status .flow-analysis .fa-lightbulb-o.recommendations { + border: 1px solid #176e83; + border-radius: 15px; +} + +#flow-status .flow-analysis .fa-lightbulb-o.violations { + border: 0; + padding: 4px; + color: white; + width: 14px; + height: 14px; + background-color: #ba554a; + border-radius: 15px; +} + +#flow-analysis-drawer { + background-color: rgba(249, 250, 251, 0.9); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25); + height: calc(100vh - 115px); + overflow-y: scroll; + padding: 20px; + position: fixed; + right: -400px; + transition: right 0.3s ease-in-out; + width: 341px; + z-index: 2; +} + +#flow-analysis-drawer.opened { + right: 0; +} + +.flow-analysis-header { + align-items: center; + display: flex; + width: 100%; +} + +.flow-analysis-loading-container { + background-color: transparent; + float: left; + height: 16px; + margin-left: 0; + margin-right: 5px; + margin-top: 0; + width: 16px; +} + +.flow-analysis-loading-message { + display: none; +} + +.flow-analysis-flow-guide-title { + color: #728e9b; + font-family: Roboto Slab; + font-size: 16px; + font-stretch: normal; + font-style: normal; + font-weight: 500; + letter-spacing: normal; + line-height: normal; + margin-bottom: 5px; +} + +.flow-analysis-flow-guide { + align-items: center; + display: flex; + justify-content: space-between; +} + +.flow-analysis-violations-options { + align-items: center; + display: flex; + margin-bottom: 4px; +} + +.flow-analysis-refresh { + align-items: center; + display: flex; +} + +.flow-analysis-flow-guide-container { + margin-top: 22px; +} + +#flow-analysis-drawer .flow-analysis-rules-accordion { + margin-top: 10px; + padding: 0 0 50px 0; +} + +#flow-analysis-drawer + .flow-analysis-rules-accordion + .ui-accordion-header, +#flow-analysis-drawer .rules-violations-header, +#flow-analysis-drawer .rules-warnings-header { + background-color: transparent; + border: 0; + color: black; + display: flex; + font-family: Roboto Slab; + font-size: 12px; + font-weight: 500; + justify-content: space-between; + padding: 10px 0; + border-bottom: 1px solid #ddd; + align-items: center; +} + +.flow-analysis-rules-accordion .ui-accordion-header-icon { + order: 2; +} + +#flow-analysis-drawer .recs-poliicies-accordion-header-text { + color: #262626; + font-family: Roboto Slab; + font-size: 12px; + font-weight: 500; + letter-spacing: 0.01px; + margin: 6px 5px 6px 0; +} + +#flow-analysis-drawer .recommended-rules-list, +#flow-analysis-drawer .required-rules-list, +#flow-analysis-drawer .rule-warnings-list { + padding: 0; + background-color: transparent; + border: 0; +} + +#flow-analysis-drawer .ui-icon { + background-image: none; + text-indent: 0; +} + +.required-rule-count, +.recommended-rule-count, +.rule-violation-count, +.rule-warning-count { + font-family: Roboto; + font-size: 14px; + font-weight: 400; +} + +#flow-analysis-drawer .rules-list-item { + border-bottom: 1px solid #ddd; +} + +#flow-analysis-drawer .rules-list-rule-info { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0 10px 10px; + font-family: Roboto; + font-size: 12px; + font-weight: normal; + font-stretch: normal; + font-style: normal; + line-height: normal; + letter-spacing: normal; + text-align: left; + color: #262626; +} + +#flow-analysis-drawer .show-only-violations-label, +#flow-analysis-drawer .show-only-warnings-label { + font-family: Roboto; + font-size: 13px; + font-weight: normal; + font-stretch: normal; + font-style: normal; + line-height: normal; + letter-spacing: normal; + text-align: left; + color: #262626; +} + +#flow-analysis-drawer .rules-list-item-menu-target { + font-size: 14px; + color: #054849; +} + +#flow-analysis-drawer .rule-menu-btn, +#flow-analysis-drawer .violation-menu-btn { + border: 0; +} + +#flow-analysis-drawer .rule-menu, +#flow-analysis-drawer .violation-menu { + width: 200px; + box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.3); + border: solid 1px #004849; + background-color: #fff; +} + +#flow-analysis-drawer .rule-menu-option, +#flow-analysis-drawer .violation-menu-option { + padding: 3px 0 3px 10px; + background-color: #fff; + font-family: Roboto; + font-size: 13px; + font-weight: normal; + font-stretch: normal; + font-style: normal; + line-height: 1.15; + letter-spacing: normal; + text-align: left; + color: #262626; + display: flex; + align-items: center; + margin: 3px 0; +} + +#flow-analysis-drawer .rule-menu-option.hidden, +#flow-analysis-drawer .violation-menu-option.hidden { + display: none; +} + +#flow-analysis-drawer .rule-menu-option:hover:not(.disabled), +#flow-analysis-drawer .violation-menu-option:hover:not(.disabled) { + background-color: #dce3e6; + color: #262626; + cursor: pointer; +} + +#flow-analysis-drawer .violation-menu-option.disabled, +#flow-analysis-drawer .rule-menu-option.disabled, +#flow-analysis-drawer .violation-menu-option-icon, +#flow-analysis-drawer .rule-menu-option-icon { + float: none; +} + +#flow-analysis-drawer .rule-menu-option-icon, +#flow-analysis-drawer .violation-menu-option-icon { + color: #004849; + font-size: 18px; + margin-right: 10px; +} + +/* Rule Information Modal */ +.rule-info-head, +.violation-info-head { + margin-top: 20px; + display: flex; + justify-content: space-between; + margin-bottom: 5px; +} + +.rule-name, +.violation-name { + align-self: flex-end; + font-family: Roboto Slab; + font-size: 12px; + letter-spacing: 0.3px; + text-align: left; + color: #262626; +} + +.rule-type-pill, +.violation-type-pill { + padding: 5px 10px; + border-radius: 12px; + font-family: Roboto; + font-size: 12px; + font-weight: 500; + letter-spacing: 0.12px; +} + +.rule-type-pill.enforce, +.violation-type-pill.enforce { + background-color: #ba554a; + color: #fff; +} + +.rule-type-pill.warn, +.violation-type-pill.warn { + border: solid 1px #176e83; + background-color: #fff; +} + +.rule-name, +.violation-name { + font-family: Roboto Slab; + font-size: 12px; + line-height: normal; +} + +.rule-display-name, +.violation-display-name { + font-family: Roboto; + font-size: 13px; + font-weight: 500; + color: #775351; + margin-bottom: 20px; +} + +.rule-description, +.violation-description { + font-family: Roboto; + font-size: 13px; + color: #262626; + margin: 20px 0; +} + +.fa.rule-docs-link-icon, +.fa.violation-docs-link-icon { + margin: 0 5px 0 0; + font-family: FontAwesome; + font-size: 16px; + color: #004849; +} + +.rule-docs-link, +.violation-docs-link { + font-family: Roboto; + font-size: 13px; + color: #004849; +} + +/* Violations */ + +.rule-violations-count { + font-family: Roboto; + font-size: 12px; + font-weight: 500; + color: #ba554a; + margin-left: 10px; +} + +.rule-recommendations-count { + font-family: Roboto; + font-size: 12px; + font-weight: 500; + color: #176e83; + margin-left: 10px; +} + +.rule-violations-list, +.rule-warnings-list, +.rule-recommendations-list { + list-style: none; +} + +.rule-violations-list li, +.rule-warnings-list li, +.rule-recommendations-list li { + border-bottom: 1px solid #ddd; +} + +.violation-menu-btn, +.recommendation-menu-btn { + margin-left: auto; + border: 0; +} + +.violation-list-item, +.recommendation-list-item, +.rule-violations-list-item-wrapper, +.warning-list-item { + padding: 10px 0 10px 15px; + display: flex; + align-items: center; + position: relative; +} + +.violation-list-item-name::before, +.warning-list-item-name::before, +.recommendation-list-item-name::before { + content: ''; + position: absolute; + left: 9px; + top: 13px; + width: 6px; + height: 6px; + border-radius: 50%; +} + +.violation-list-item-name::before, +.rule-violations-list-item-wrapper:before { + background-color: #ba554a; +} + +.recommendation-list-item-name::before, +.rule-warnings-list-item-name::before, +.warning-list-item-name::before, +.recommendation-list-item-name::before { + background-color: transparent; + border: 1px solid #176e83; +} + +.violation-list-item-wrapper, +.recommendation-list-item-wrapper, +.warning-list-item-wrapper { + display: flex; + flex-direction: column; + align-items: flex-start; + width: calc(100% - 5px); + margin-left: 5px; +} + +.violation-list-item-name, +.recommendation-list-item-name, +.warning-list-item-name { + font-family: Roboto; + font-size: 11px; + font-weight: 500; +} + +.rule-violations-list-item-name, +.rule-warnings-list-item-name { + font-family: Roboto; + font-size: 12px; + font-weight: 500; + margin-top: 11px; +} + +.rule-violations-list-item-name { + color: #ba554a; +} + +.rule-warnings-list-item-name { + color: #176e83; +} + +.violation-list-item-id, +.recommendation-list-item-id { + font-size: 11px; + line-height: normal; + color: #606060; +} diff --git a/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 b/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 index 58e99759ac..5ac793a650 100644 --- a/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 +++ b/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 @@ -29,9 +29,10 @@ 'nf.Settings', 'nf.ParameterContexts', 'nf.ProcessGroup', - 'nf.ProcessGroupConfiguration'], - function ($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration) { - return (nf.ng.Canvas.FlowStatusCtrl = factory($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration)); + 'nf.ProcessGroupConfiguration', + 'nf.Shell'], + function ($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration, nfShell) { + return (nf.ng.Canvas.FlowStatusCtrl = factory($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration, nfShell)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.ng.Canvas.FlowStatusCtrl = @@ -45,7 +46,8 @@ require('nf.Settings'), require('nf.ParameterContexts'), require('nf.ProcessGroup'), - require('nf.ProcessGroupConfiguration'))); + require('nf.ProcessGroupConfiguration'), + require('nf.Shell'))); } else { nf.ng.Canvas.FlowStatusCtrl = factory(root.$, root.nf.Common, @@ -57,9 +59,10 @@ root.nf.Settings, root.nf.ParameterContexts, root.nf.ProcessGroup, - root.nf.ProcessGroupConfiguration); + root.nf.ProcessGroupConfiguration, + root.nf.Shell); } -}(this, function ($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration) { +}(this, function ($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration, nfShell) { 'use strict'; return function (serviceProvider) { @@ -69,10 +72,13 @@ search: 'Search', urls: { search: '../nifi-api/flow/search-results', - status: '../nifi-api/flow/status' + status: '../nifi-api/flow/status', + flowAnalysis: '../nifi-api/controller/analyze-flow' } }; + var previousRulesResponse = {}; + function FlowStatusCtrl() { this.connectedNodesCount = "-"; this.clusterConnectionWarning = false; @@ -400,6 +406,561 @@ } } + /** + * 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(); + ruleWarningListEl.empty(); + violations.forEach(function(violation) { + var rule = rules.find(function(rule) { + return rule.id === violation.ruleId; + }); + // create DOM elements + var violationListItemEl = $('
    • '); + var violationEl = $('
      '); + var violationListItemWrapperEl = $('
      '); + var violationRuleEl = $('
      '); + var violationListItemNameEl = $('
      '); + var violationListItemIdEl = $(''); + var violationInfoButtonEl = $(''); + + // add text content and button data + $(violationRuleEl).text(rule.name); + violation.subjectPermissionDto.canRead ? $(violationListItemNameEl).text(violation.subjectDisplayName) : $(violationListItemNameEl).text('Unauthorized').addClass('unauthorized'); + $(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 = $('
    • '); + var warningEl = $('
      '); + var warningListItemWrapperEl = $('
      '); + var warningRuleEl = $('
      '); + var warningListItemNameEl = $('
      '); + var warningListItemIdEl = $(''); + var warningInfoButtonEl = $(''); + + // add text content and button data + $(warningRuleEl).text(rule.name); + warning.subjectPermissionDto.canRead ? $(warningListItemNameEl).text(warning.subjectDisplayName) : $(warningListItemNameEl).text('Unauthorized').addClass('unauthorized'); + $(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 = $('
    • ').append($(rec.requirement).append(rec.requirementInfoButton)) + var violationsListEl = ''; + var violationCountEl = ''; + + var violations = violationsMap.get(rec.id); + if (!!violations) { + if (violations.length === 1) { + violationCountEl = '
      ' + violations.length + ' ' + ruleType + '
      '; + } else { + violationCountEl = '
      ' + violations.length + ' ' + ruleType + 's
      '; + } + violationsListEl = $('
        '); + violations.forEach(function(violation) { + // create DOM elements + var violationListItemEl = $('
      • '); + var violationWrapperEl = $('
        '); + var violationNameEl = $('
        '); + var violationIdEl = $(''); + var violationInfoButtonEl = $(''); + + // add text content and button data + violation.subjectPermissionDto.canRead ? violationNameEl.text(violation.subjectDisplayName) : violationNameEl.text('Unauthorized'); + 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 flowAnalysisLoader = $('#flow-analysis-loading-container'); + var flowAnalysisLoadMessage = $('#flow-analysis-loading-message'); + + 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); + + if (response.flowAnalysisPending) { + flowAnalysisLoader.addClass('ajax-loading'); + flowAnalysisLoadMessage.show(); + } else { + flowAnalysisLoader.removeClass('ajax-loading'); + flowAnalysisLoadMessage.hide(); + } + + // 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 = '
        '; + var requirementName = $('
        ').text(rule.name); + var requirementInfoButton = ''; + 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 = '
        '; + var requirementName = $('
        ').text(rule.name); + var requirementInfoButton = ''; + 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(); + + // setup violation menu handling + flowAnalysisCtrl.setViolationMenuHandling(response.rules); + } + }).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(); + // unbind previously bound rule data that may still exist + unbindRuleMenuHandling(); + + 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 + if (nfCommon.canAccessController()) { + $('#rule-menu-edit-rule').removeClass('disabled'); + $('#rule-menu-edit-rule .rule-menu-option-icon').removeClass('disabled'); + $('#rule-menu-edit-rule').on('click', openRuleDetailsDialog); + } else { + $('#rule-menu-edit-rule').addClass('disabled'); + $('#rule-menu-edit-rule .rule-menu-option-icon').addClass('disabled'); + } + $('#rule-menu-view-documentation').on('click', viewRuleDocumentation); + $(document).on('click', closeRuleWindow); + + function viewRuleDocumentation(e) { + nfShell.showPage('../nifi-docs/documentation?' + $.param({ + select: ruleInfo.type, + group: ruleInfo.bundle.group, + artifact: ruleInfo.bundle.artifact, + version: ruleInfo.bundle.version + })).done(function () {}); + $("#rule-menu").hide(); + unbindRuleMenuHandling(); + } + + 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 unbindRuleMenuHandling() { + $('#rule-menu-edit-rule').off("click"); + $('#rule-menu-view-documentation').off("click"); + $(document).unbind('click', closeRuleWindow); + } + + }); + }, + + /** + * Set event bindings for violation menus + */ + setViolationMenuHandling: function(rules) { + $('.violation-menu-btn').click(function(event) { + // stop event from immediately bubbling up to document and triggering closeViolationWindow + event.stopPropagation(); + var violationInfo = $(this).data('violationInfo'); + $('#rule-menu').hide(); + $('#violation-menu').show(); + $('#violation-menu').position({ + my: "left top", + at: "left top", + of: event + }); + + // violation menu bindings + if (violationInfo.subjectPermissionDto.canRead) { + $('#violation-menu-more-info').removeClass('disabled'); + $('#violation-menu-more-info .violation-menu-option-icon').removeClass('disabled'); + $('#violation-menu-more-info').on( "click", openRuleViolationMoreInfoDialog); + } else { + $('#violation-menu-more-info').addClass('disabled'); + $('#violation-menu-more-info .violation-menu-option-icon').addClass('disabled'); + } + + if (violationInfo.subjectComponentType === 'PROCESSOR' && violationInfo.subjectPermissionDto.canRead) { + $('#violation-menu-go-to').removeClass('hidden'); + $('#violation-menu-go-to').on('click', goToComponent); + } else { + $('#violation-menu-go-to').addClass('hidden'); + } + $(document).on('click', closeViolationWindow); + + function closeViolationWindow(e) { + if ($(e.target).parents("#violation-menu").length === 0) { + $("#violation-menu").hide(); + unbindViolationMenuHandling(); + } + } + + function openRuleViolationMoreInfoDialog() { + var rule = rules.find(function(rule){ + return rule.id === violationInfo.ruleId; + }); + $('#violation-menu').hide(); + $('#violation-type-pill').empty() + .removeClass() + .addClass(violationInfo.enforcementPolicy.toLowerCase() + ' violation-type-pill') + .append(violationInfo.enforcementPolicy); + $('#violation-description').empty().append(violationInfo.violationMessage); + $('#violation-menu-more-info-dialog').modal( "show" ); + $('.violation-docs-link').click(function () { + // open the documentation for this flow analysis rule + nfShell.showPage('../nifi-docs/documentation?' + $.param({ + select: rule.type, + group: rule.bundle.group, + artifact: rule.bundle.artifact, + version: rule.bundle.version + })).done(function () {}); + }); + unbindViolationMenuHandling(); + } + + function goToComponent() { + $('#violation-menu').hide(); + nfCanvasUtils.showComponent(violationInfo.groupId, violationInfo.subjectId); + unbindViolationMenuHandling(); + } + + function unbindViolationMenuHandling() { + $('#violation-menu-more-info').off("click"); + $('#violation-menu-go-to').off("click"); + $(document).unbind('click', closeViolationWindow); + } + }); + }, + + /** + * Initialize the flow analysis controller. + */ + init: function () { + var flowAnalysisCtrl = this; + var drawer = $('#flow-analysis-drawer'); + var requiredRulesEl = $('#required-rules'); + var recommendedRulesEl = $('#recommended-rules'); + var flowAnalysisRefreshIntervalSeconds = nfCommon.getAutoRefreshInterval(); + + $('#flow-analysis').click(function () { + $(this).toggleClass('opened'); + drawer.toggleClass('opened'); + }); + requiredRulesEl.accordion({ + collapsible: true, + active: false, + icons: { + "header": "fa fa-chevron-down", + "activeHeader": "fa fa-chevron-up" + } + }); + + recommendedRulesEl.accordion({ + collapsible: true, + active: false, + icons: { + "header": "fa fa-chevron-down", + "activeHeader": "fa fa-chevron-up" + } + }); + $('#rule-menu').hide(); + $('#violation-menu').hide(); + $('#rule-menu-more-info-dialog').modal({ + scrollableContentStyle: 'scrollable', + headerText: 'Rule Information', + buttons: [{ + buttonText: 'OK', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + $(this).modal('hide'); + } + } + }], + handler: { + close: function () {} + } + }); + $('#violation-menu-more-info-dialog').modal({ + scrollableContentStyle: 'scrollable', + headerText: 'Violation Information', + buttons: [{ + buttonText: 'OK', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + $(this).modal('hide'); + } + } + }], + handler: { + close: function () {} + } + }); + + this.loadFlowPolicies(); + setInterval(this.loadFlowPolicies.bind(this), flowAnalysisRefreshIntervalSeconds * 1000); + + this.toggleOnlyViolations(false); + this.toggleOnlyWarnings(false); + // handle show only violations checkbox + $('#show-only-violations').on('change', function(event) { + var isChecked = $(this).hasClass('checkbox-checked'); + flowAnalysisCtrl.toggleOnlyViolations(isChecked); + }); + + $('#show-only-warnings').on('change', function(event) { + var isChecked = $(this).hasClass('checkbox-checked'); + flowAnalysisCtrl.toggleOnlyWarnings(isChecked); + }); + }, + + /** + * Show/hide violations menu + */ + toggleOnlyViolations: function(isViolationsChecked) { + var requiredRulesEl = $('#required-rules'); + var recommendedRulesEl = $('#recommended-rules'); + var ruleViolationsEl = $('#rule-violations'); + + var isWarningsChecked = $('#show-only-warnings').hasClass( + 'checkbox-checked' + ); + + isViolationsChecked + ? ruleViolationsEl.show() + : ruleViolationsEl.hide(); + if (isViolationsChecked || isWarningsChecked) { + requiredRulesEl.hide(); + recommendedRulesEl.hide(); + } else { + requiredRulesEl.show(); + recommendedRulesEl.show(); + } + this.loadFlowPolicies(); + }, + + /** + * Show/hide warnings menu + */ + toggleOnlyWarnings: function (isWarningsChecked) { + var requiredRulesEl = $('#required-rules'); + var recommendedRulesEl = $('#recommended-rules'); + var ruleWarningsEl = $('#rule-warnings'); + var isViolationsChecked = $('#show-only-violations').hasClass( + 'checkbox-checked' + ); + + isWarningsChecked + ? ruleWarningsEl.show() + : ruleWarningsEl.hide(); + if (isWarningsChecked || isViolationsChecked) { + requiredRulesEl.hide(); + recommendedRulesEl.hide(); + } else { + requiredRulesEl.show(); + recommendedRulesEl.show(); + } + this.loadFlowPolicies(); + }, + } + /** * The bulletins controller. */ @@ -468,6 +1029,7 @@ */ init: function () { this.search.init(); + this.flowAnalysis.init(); }, /** @@ -684,6 +1246,14 @@ */ updateBulletins: function (response) { this.bulletins.update(response); + }, + + /** + * Reloads flow analysis rules + * + */ + reloadFlowPolicies: function () { + this.flowAnalysis.loadFlowPolicies(); } } 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 458f6d84f0..f8f6b99298 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 @@ -1223,6 +1223,7 @@ */ reload: function () { nfCanvasUtils.reload(); + nfNgBridge.injector.get('flowStatusCtrl').reloadFlowPolicies(); }, /** 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 8e5afb46a5..c120b6f5a5 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 @@ -333,7 +333,7 @@ // get the auto refresh interval var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10); - + nfCommon.setAutoRefreshInterval(autoRefreshIntervalSeconds); // record whether we can configure the authorizer nfCanvas.setManagedAuthorizer(configDetails.supportsManagedAuthorizer); nfCanvas.setConfigurableAuthorizer(configDetails.supportsConfigurableAuthorizer); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-analysis-rule.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-analysis-rule.js index b59b3c070b..72874d4005 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-analysis-rule.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-analysis-rule.js @@ -464,11 +464,11 @@ value: 'ENFORCE', description: 'Treat violations of this rule as errors the correction of which is mandatory.' } -// , { -// text: 'Warn', -// value: 'WARN', -// description: 'Treat violations of by this rule as warnings the correction of which is recommended but not mandatory.' -// } + , { + text: 'Warn', + value: 'WARN', + description: 'Treat violations of by this rule as warnings the correction of which is recommended but not mandatory.' + } ], selectedOption: { value: flowAnalysisRule['enforcementPolicy'] diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 3c6d0b1115..a3e37d2855 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -173,6 +173,7 @@ var nfCommon = { ANONYMOUS_USER_TEXT: 'Anonymous user', + autoRefreshInterval: null, config: { sensitiveText: 'Sensitive value set', @@ -1895,6 +1896,24 @@ }); return sortedAuthorizedParameterContexts.concat(sortedUnauthorizedParameterContexts); + }, + + /** + * Sets the global auto refresh value + * + * @param {integer} interval in seconds The numeric value for the auto refresh interval + */ + setAutoRefreshInterval: function (interval) { + nfCommon.config.autoRefreshInterval = interval; + }, + + /** + * Gets the global auto refresh value + * + * @returns {integer} + */ + getAutoRefreshInterval: function () { + return nfCommon.config.autoRefreshInterval; } };