mirror of https://github.com/apache/nifi.git
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
This commit is contained in:
parent
3f7085fec8
commit
4104cfd78d
|
@ -741,6 +741,7 @@
|
||||||
<include>${staging.dir}/css/settings.css</include>
|
<include>${staging.dir}/css/settings.css</include>
|
||||||
<include>${staging.dir}/css/about.css</include>
|
<include>${staging.dir}/css/about.css</include>
|
||||||
<include>${staging.dir}/css/status-history.css</include>
|
<include>${staging.dir}/css/status-history.css</include>
|
||||||
|
<include>${staging.dir}/css/flow-analysis-drawer.css</include>
|
||||||
</includes>
|
</includes>
|
||||||
</aggregation>
|
</aggregation>
|
||||||
<aggregation>
|
<aggregation>
|
||||||
|
@ -780,6 +781,7 @@
|
||||||
<include>${staging.dir}/css/processor-details.css</include>
|
<include>${staging.dir}/css/processor-details.css</include>
|
||||||
<include>${staging.dir}/css/connection-details.css</include>
|
<include>${staging.dir}/css/connection-details.css</include>
|
||||||
<include>${staging.dir}/css/status-history.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>
|
<include>${staging.dir}/css/summary.css</include>
|
||||||
</includes>
|
</includes>
|
||||||
</aggregation>
|
</aggregation>
|
||||||
|
|
|
@ -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/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/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/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" />
|
<link rel="stylesheet" href="css/summary.css?${project.version}" type="text/css" />
|
|
@ -128,6 +128,7 @@
|
||||||
<jsp:include page="/WEB-INF/partials/canvas/registry-configuration-dialog.jsp"/>
|
<jsp:include page="/WEB-INF/partials/canvas/registry-configuration-dialog.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/canvas/new-registry-client-dialog.jsp"/>
|
<jsp:include page="/WEB-INF/partials/canvas/new-registry-client-dialog.jsp"/>
|
||||||
<div id="canvas-container" class="unselectable"></div>
|
<div id="canvas-container" class="unselectable"></div>
|
||||||
|
<jsp:include page="/WEB-INF/partials/canvas/flow-analysis-drawer.jsp"/>
|
||||||
<div id="canvas-tooltips">
|
<div id="canvas-tooltips">
|
||||||
<div id="processor-tooltips"></div>
|
<div id="processor-tooltips"></div>
|
||||||
<div id="port-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/parameter-provider-configuration.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/canvas/processor-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/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/process-group-configuration.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/canvas/override-policy-dialog.jsp"/>
|
<jsp:include page="/WEB-INF/partials/canvas/override-policy-dialog.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/canvas/policy-management.jsp"/>
|
<jsp:include page="/WEB-INF/partials/canvas/policy-management.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>
|
|
@ -71,6 +71,7 @@
|
||||||
<button id="search-button" ng-click="appCtrl.serviceProvider.headerCtrl.flowStatusCtrl.search.toggleSearchField();"><i class="fa fa-search"></i></button>
|
<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"/>
|
<input id="search-field" type="text" placeholder="Search"/>
|
||||||
</div>
|
</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>
|
<button id="bulletin-button"><i class="fa fa-sticky-note-o"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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>
|
|
@ -53,3 +53,4 @@
|
||||||
@import url(status-history.css);
|
@import url(status-history.css);
|
||||||
@import url(../fonts/flowfont/flowfont.css);
|
@import url(../fonts/flowfont/flowfont.css);
|
||||||
@import url(../assets/font-awesome/css/font-awesome.css);
|
@import url(../assets/font-awesome/css/font-awesome.css);
|
||||||
|
@import url(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;
|
||||||
|
}
|
|
@ -29,9 +29,10 @@
|
||||||
'nf.Settings',
|
'nf.Settings',
|
||||||
'nf.ParameterContexts',
|
'nf.ParameterContexts',
|
||||||
'nf.ProcessGroup',
|
'nf.ProcessGroup',
|
||||||
'nf.ProcessGroupConfiguration'],
|
'nf.ProcessGroupConfiguration',
|
||||||
function ($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration) {
|
'nf.Shell'],
|
||||||
return (nf.ng.Canvas.FlowStatusCtrl = factory($, nfCommon, nfDialog, nfCanvasUtils, nfContextMenu, nfClusterSummary, nfErrorHandler, nfSettings, nfParameterContexts, nfProcessGroup, nfProcessGroupConfiguration));
|
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') {
|
} else if (typeof exports === 'object' && typeof module === 'object') {
|
||||||
module.exports = (nf.ng.Canvas.FlowStatusCtrl =
|
module.exports = (nf.ng.Canvas.FlowStatusCtrl =
|
||||||
|
@ -45,7 +46,8 @@
|
||||||
require('nf.Settings'),
|
require('nf.Settings'),
|
||||||
require('nf.ParameterContexts'),
|
require('nf.ParameterContexts'),
|
||||||
require('nf.ProcessGroup'),
|
require('nf.ProcessGroup'),
|
||||||
require('nf.ProcessGroupConfiguration')));
|
require('nf.ProcessGroupConfiguration'),
|
||||||
|
require('nf.Shell')));
|
||||||
} else {
|
} else {
|
||||||
nf.ng.Canvas.FlowStatusCtrl = factory(root.$,
|
nf.ng.Canvas.FlowStatusCtrl = factory(root.$,
|
||||||
root.nf.Common,
|
root.nf.Common,
|
||||||
|
@ -57,9 +59,10 @@
|
||||||
root.nf.Settings,
|
root.nf.Settings,
|
||||||
root.nf.ParameterContexts,
|
root.nf.ParameterContexts,
|
||||||
root.nf.ProcessGroup,
|
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';
|
'use strict';
|
||||||
|
|
||||||
return function (serviceProvider) {
|
return function (serviceProvider) {
|
||||||
|
@ -69,10 +72,13 @@
|
||||||
search: 'Search',
|
search: 'Search',
|
||||||
urls: {
|
urls: {
|
||||||
search: '../nifi-api/flow/search-results',
|
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() {
|
function FlowStatusCtrl() {
|
||||||
this.connectedNodesCount = "-";
|
this.connectedNodesCount = "-";
|
||||||
this.clusterConnectionWarning = false;
|
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.
|
* The bulletins controller.
|
||||||
*/
|
*/
|
||||||
|
@ -468,6 +1029,7 @@
|
||||||
*/
|
*/
|
||||||
init: function () {
|
init: function () {
|
||||||
this.search.init();
|
this.search.init();
|
||||||
|
this.flowAnalysis.init();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -684,6 +1246,14 @@
|
||||||
*/
|
*/
|
||||||
updateBulletins: function (response) {
|
updateBulletins: function (response) {
|
||||||
this.bulletins.update(response);
|
this.bulletins.update(response);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads flow analysis rules
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
reloadFlowPolicies: function () {
|
||||||
|
this.flowAnalysis.loadFlowPolicies();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1223,6 +1223,7 @@
|
||||||
*/
|
*/
|
||||||
reload: function () {
|
reload: function () {
|
||||||
nfCanvasUtils.reload();
|
nfCanvasUtils.reload();
|
||||||
|
nfNgBridge.injector.get('flowStatusCtrl').reloadFlowPolicies();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -333,7 +333,7 @@
|
||||||
|
|
||||||
// get the auto refresh interval
|
// get the auto refresh interval
|
||||||
var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
|
var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
|
||||||
|
nfCommon.setAutoRefreshInterval(autoRefreshIntervalSeconds);
|
||||||
// record whether we can configure the authorizer
|
// record whether we can configure the authorizer
|
||||||
nfCanvas.setManagedAuthorizer(configDetails.supportsManagedAuthorizer);
|
nfCanvas.setManagedAuthorizer(configDetails.supportsManagedAuthorizer);
|
||||||
nfCanvas.setConfigurableAuthorizer(configDetails.supportsConfigurableAuthorizer);
|
nfCanvas.setConfigurableAuthorizer(configDetails.supportsConfigurableAuthorizer);
|
||||||
|
|
|
@ -464,11 +464,11 @@
|
||||||
value: 'ENFORCE',
|
value: 'ENFORCE',
|
||||||
description: 'Treat violations of this rule as errors the correction of which is mandatory.'
|
description: 'Treat violations of this rule as errors the correction of which is mandatory.'
|
||||||
}
|
}
|
||||||
// , {
|
, {
|
||||||
// text: 'Warn',
|
text: 'Warn',
|
||||||
// value: 'WARN',
|
value: 'WARN',
|
||||||
// description: 'Treat violations of by this rule as warnings the correction of which is recommended but not mandatory.'
|
description: 'Treat violations of by this rule as warnings the correction of which is recommended but not mandatory.'
|
||||||
// }
|
}
|
||||||
],
|
],
|
||||||
selectedOption: {
|
selectedOption: {
|
||||||
value: flowAnalysisRule['enforcementPolicy']
|
value: flowAnalysisRule['enforcementPolicy']
|
||||||
|
|
|
@ -173,6 +173,7 @@
|
||||||
|
|
||||||
var nfCommon = {
|
var nfCommon = {
|
||||||
ANONYMOUS_USER_TEXT: 'Anonymous user',
|
ANONYMOUS_USER_TEXT: 'Anonymous user',
|
||||||
|
autoRefreshInterval: null,
|
||||||
|
|
||||||
config: {
|
config: {
|
||||||
sensitiveText: 'Sensitive value set',
|
sensitiveText: 'Sensitive value set',
|
||||||
|
@ -1895,6 +1896,24 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
return sortedAuthorizedParameterContexts.concat(sortedUnauthorizedParameterContexts);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue