This is an automated email from the ASF dual-hosted git repository.
mcgilman pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 4104cfd78d NIFI-11520: Add a menu to display Flow Analysis report
results (#8273)
4104cfd78d is described below
commit 4104cfd78d829ec4ab6bf4470ae472a6eda74171
Author: Shane Ardell <[email protected]>
AuthorDate: Tue Apr 16 17:34:19 2024 -0400
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-framework/nifi-web/nifi-web-ui/pom.xml | 2 +
.../src/main/resources/filters/summary.properties | 1 +
.../src/main/webapp/WEB-INF/pages/canvas.jsp | 2 +
.../partials/canvas/flow-analysis-drawer.jsp | 84 +++
.../webapp/WEB-INF/partials/canvas/flow-status.jsp | 1 +
.../canvas/violation-description-dialog.jsp | 28 +
.../nifi-web-ui/src/main/webapp/css/canvas.css | 1 +
.../src/main/webapp/css/flow-analysis-drawer.css | 463 ++++++++++++++++
.../nf-ng-canvas-flow-status-controller.js | 584 ++++++++++++++++++++-
.../src/main/webapp/js/nf/canvas/nf-actions.js | 1 +
.../webapp/js/nf/canvas/nf-canvas-bootstrap.js | 2 +-
.../webapp/js/nf/canvas/nf-flow-analysis-rule.js | 10 +-
.../nifi-web-ui/src/main/webapp/js/nf/nf-common.js | 19 +
13 files changed, 1185 insertions(+), 13 deletions(-)
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 @@
<include>${staging.dir}/css/settings.css</include>
<include>${staging.dir}/css/about.css</include>
<include>${staging.dir}/css/status-history.css</include>
+
<include>${staging.dir}/css/flow-analysis-drawer.css</include>
</includes>
</aggregation>
<aggregation>
@@ -780,6 +781,7 @@
<include>${staging.dir}/css/processor-details.css</include>
<include>${staging.dir}/css/connection-details.css</include>
<include>${staging.dir}/css/status-history.css</include>
+
<include>${staging.dir}/css/flow-analysis-drawer.css</include>
<include>${staging.dir}/css/summary.css</include>
</includes>
</aggregation>
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=<link rel="stylesheet"
href="css/main.css?${project.versio
<link rel="stylesheet" href="css/connection-details.css?${project.version}"
type="text/css" />\n\
<link rel="stylesheet" href="css/message-pane.css?${project.version}"
type="text/css" />\n\
<link rel="stylesheet" href="css/status-history.css?${project.version}"
type="text/css" />\n\
+<link rel="stylesheet" href="css/flow-analysis-drawer.css?${project.version}"
type="text/css" />\n\
<link rel="stylesheet" href="css/summary.css?${project.version}"
type="text/css" />
\ 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 @@
<jsp:include
page="/WEB-INF/partials/canvas/registry-configuration-dialog.jsp"/>
<jsp:include
page="/WEB-INF/partials/canvas/new-registry-client-dialog.jsp"/>
<div id="canvas-container" class="unselectable"></div>
+ <jsp:include page="/WEB-INF/partials/canvas/flow-analysis-drawer.jsp"/>
<div id="canvas-tooltips">
<div id="processor-tooltips"></div>
<div id="port-tooltips"></div>
@@ -145,6 +146,7 @@
<jsp:include
page="/WEB-INF/partials/canvas/parameter-provider-configuration.jsp"/>
<jsp:include
page="/WEB-INF/partials/canvas/processor-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/processor-details.jsp"/>
+ <jsp:include
page="/WEB-INF/partials/canvas/violation-description-dialog.jsp"/>
<jsp:include
page="/WEB-INF/partials/canvas/process-group-configuration.jsp"/>
<jsp:include
page="/WEB-INF/partials/canvas/override-policy-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/policy-management.jsp"/>
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" %>
+<section id="flow-analysis-drawer">
+ <div class="flow-analysis-header">
+ <div id="flow-analysis-loading-container"
class="flow-analysis-loading-container"></div>
+ <div id="flow-analysis-loading-message"
class="flow-analysis-loading-message">Rules analysis pending...</div>
+ </div>
+ <div class="flow-analysis-flow-guide-container">
+ <div class="flow-analysis-flow-guide">
+ <div class="flow-analysis-flow-guide-title">Flow Guide</div>
+ <div>
+ <div class="flow-analysis-violations-options">
+ <div class="nf-checkbox checkbox-unchecked"
id="show-only-violations"></div>
+ <span class="nf-checkbox-label
show-only-violations-label">Show enforced violations</span>
+ </div>
+ <div class="flow-analysis-warnings-options">
+ <div class="nf-checkbox checkbox-unchecked"
id="show-only-warnings"></div>
+ <span class="nf-checkbox-label
show-only-warnings-label">Show warning violations</span>
+ </div>
+ </div>
+ </div>
+ <div class="flow-analysis-flow-guide-breadcrumb">NiFi Flow</div>
+ </div>
+ <div id="flow-analysis-rules-accordion"
class="flow-analysis-rules-accordion">
+
+ <div id="required-rules" class="required-rules">
+ <div>
+ <div>Enforced Rules <span id="required-rule-count"
class="required-rule-count"></span></div>
+ </div>
+ <ul id="required-rules-list" class="required-rules-list">
+ </ul>
+ </div>
+
+ <div id="recommended-rules" class="recommended-rules">
+ <div>
+ <div>Warning Rules <span id="recommended-rule-count"
class="recommended-rule-count"></span></div>
+ </div>
+ <ul id="recommended-rules-list"
class="recommended-rules-list"></ul>
+ </div>
+
+ <div id="rule-violations" class="rule-violations">
+ <div class="rules-violations-header">
+ <div>Enforced Violations <span id="rule-violation-count"
class="rule-violation-count"></span></div>
+ </div>
+ <ul id="rule-violations-list" class="rule-violations-list"></ul>
+ </div>
+
+ <div id="rule-warnings" class="rule-warnings">
+ <div class="rules-warnings-header">
+ <div>Warning Violations <span id="rule-warning-count"
class="rule-warning-count"></span></div>
+ </div>
+ <ul id="rule-warnings-list" class="rule-warnings-list"></ul>
+ </div>
+
+ <div class="rule-menu" id="rule-menu">
+ <ul>
+ <li class="rule-menu-option"
id="rule-menu-view-documentation"><i class="fa fa-info-circle
rule-menu-option-icon" aria-hidden="true"></i>View Documentation</li>
+ <li class="rule-menu-option" id="rule-menu-edit-rule"><i
class="fa fa-pencil rule-menu-option-icon" aria-hidden="true"></i>Edit Rule</li>
+ </ul>
+ </div>
+
+ <div class="violation-menu" id="violation-menu">
+ <ul>
+ <li class="violation-menu-option"
id="violation-menu-more-info"><i class="fa fa-info-circle
violation-menu-option-icon" aria-hidden="true"></i>Violation details</li>
+ <li class="violation-menu-option" id="violation-menu-go-to"><i
class="fa fa-pencil violation-menu-option-icon" aria-hidden="true"></i>Go to
component</li>
+ </ul>
+ </div>
+ </div>
+</section>
\ 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 @@
<button id="search-button"
ng-click="appCtrl.serviceProvider.headerCtrl.flowStatusCtrl.search.toggleSearchField();"><i
class="fa fa-search"></i></button>
<input id="search-field" type="text" placeholder="Search"/>
</div>
+ <button id="flow-analysis" class="flow-analysis"><i class="fa
fa-lightbulb-o flow-analysis-notification-icon"></i></button>
<button id="bulletin-button"><i class="fa
fa-sticky-note-o"></i></button>
</div>
</div>
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" %>
+<div id="violation-menu-more-info-dialog" layout="column" class="hidden
medium-dialog">
+ <div class="dialog-content">
+ <div class="violation-info-head">
+ <div id="violation-name" class="violation-name">Violation</div>
+ <div id="violation-type-pill" class="violation-type-pill"></div>
+ </div>
+ <div id="violation-display-name" class="violation-display-name"></div>
+
+ <p id="violation-description" class="violation-description"></p>
+
+ <i class="fa fa-book violation-docs-link-icon"
aria-hidden="true"></i><a href="" class="violation-docs-link">View
Documentation</a>
+ </div>
+</div>
\ 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 = $('<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);
+ 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 = $('<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);
+ 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 = $('<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
+ 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 = '<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();
+
+ // 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;
}
};