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/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>
|
||||
|
|
|
@ -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" />
|
|
@ -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"/>
|
||||
|
|
|
@ -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>
|
||||
<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>
|
||||
|
|
|
@ -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(../fonts/flowfont/flowfont.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.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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1223,6 +1223,7 @@
|
|||
*/
|
||||
reload: function () {
|
||||
nfCanvasUtils.reload();
|
||||
nfNgBridge.injector.get('flowStatusCtrl').reloadFlowPolicies();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue