This is an automated email from the ASF dual-hosted git repository. aichrist pushed a commit to branch analytics-framework in repository https://gitbox.apache.org/repos/asf/nifi.git
commit b74c4dfb8434650aebe350ef90a9e041cbe1980b Author: Rob Fellows <[email protected]> AuthorDate: Mon Jul 22 17:02:48 2019 -0400 NIFI-6568 - Surface time-to-back-pressure and initial predictions in the UI * Add multi-line tooltips with detail for connection queue back pressure graphics. * Add estimated time to back pressure to connections summary table. * Add back pressure prediction ticks. * add moment.js to format predicted time to back pressure * tweak summary table headings to match data displayed. re-order connection summary columns --- nifi-assembly/LICENSE | 25 ++ .../src/main/resources/META-INF/LICENSE | 25 ++ .../nifi-framework/nifi-web/nifi-web-ui/pom.xml | 4 + .../src/main/frontend/package-lock.json | 21 +- .../nifi-web-ui/src/main/frontend/package.json | 1 + .../src/main/resources/META-INF/LICENSE | 25 ++ .../main/webapp/WEB-INF/pages/bulletin-board.jsp | 1 + .../src/main/webapp/WEB-INF/pages/canvas.jsp | 2 + .../src/main/webapp/WEB-INF/pages/cluster.jsp | 1 + .../src/main/webapp/WEB-INF/pages/counters.jsp | 1 + .../src/main/webapp/WEB-INF/pages/history.jsp | 1 + .../src/main/webapp/WEB-INF/pages/login.jsp | 1 + .../src/main/webapp/WEB-INF/pages/provenance.jsp | 1 + .../src/main/webapp/WEB-INF/pages/summary.jsp | 1 + .../src/main/webapp/WEB-INF/pages/templates.jsp | 1 + .../src/main/webapp/WEB-INF/pages/users.jsp | 1 + .../nifi-web-ui/src/main/webapp/css/graph.css | 25 +- .../src/main/webapp/js/nf/canvas/nf-connection.js | 333 +++++++++++++++++---- .../nifi-web-ui/src/main/webapp/js/nf/nf-common.js | 22 +- .../main/webapp/js/nf/summary/nf-summary-table.js | 140 ++++++--- 20 files changed, 525 insertions(+), 107 deletions(-) diff --git a/nifi-assembly/LICENSE b/nifi-assembly/LICENSE index 123125b..eb00732 100644 --- a/nifi-assembly/LICENSE +++ b/nifi-assembly/LICENSE @@ -2583,6 +2583,31 @@ This product bundles 'lodash' which is available under an MIT license. licenses; we recommend you read them, as their terms may differ from the terms above. +This product bundles 'moment' which is available under an MIT license. + + Copyright (c) JS Foundation and other contributors + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + The binary distribution of this product bundles 'normalize.css' NORMALIZE.CSS LICENSE diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE index 679589f..c6530dd 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE @@ -853,3 +853,28 @@ This product bundles 'lodash' which is available under an MIT license. maintained libraries used by this software which have their own licenses; we recommend you read them, as their terms may differ from the terms above. + +This product bundles 'moment' which is available under an MIT license. + + Copyright (c) JS Foundation and other contributors + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml index ef283de..08dca29 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml @@ -239,6 +239,10 @@ <include>lodash-core/distrib/lodash-core.min.js</include> <include>lodash-core/distrib/README.md</include> + <include>moment/min/moment.min.js</include> + <include>moment/README.md</include> + <include>moment/LICENSE</include> + </includes> </resource> </resources> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json index 8238e32..9836aa9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json @@ -88,9 +88,9 @@ } }, "commander": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" }, "d3": { "version": "4.13.0", @@ -189,8 +189,8 @@ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", "requires": { - "commander": "2.16.0", - "iconv-lite": "0.4.23", + "commander": "2.20.0", + "iconv-lite": "0.4.24", "rw": "1.3.3" } }, @@ -372,9 +372,9 @@ "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=" }, "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { "safer-buffer": "2.1.2" } @@ -432,6 +432,11 @@ "resolved": "https://registry.npmjs.org/lodash-core/-/lodash-core-4.17.11.tgz", "integrity": "sha512-8ilprNE67U1REh0wQHL0z37qXdsxuFXjvxehg79Mh/MQgNJ+C1muXtysSKpt9sCxXZUSiwifEC82Vtzf2GSSKQ==" }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, "nomnom": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json index b45e1a1..2f0fb75 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json @@ -35,6 +35,7 @@ "reset.css": "2.0.2", "jquery-form": "3.50.0", "lodash-core": "4.17.11", + "moment": "2.24.0", "url-search-params": "0.6.1", "jsonlint": "1.6.2", "qtip2": "3.0.3", diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE index 9e1b605..fc0572d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE @@ -771,3 +771,28 @@ This product bundles 'lodash' which is available under an MIT license. maintained libraries used by this software which have their own licenses; we recommend you read them, as their terms may differ from the terms above. + +This product bundles 'moment' which is available under an MIT license. + + Copyright (c) JS Foundation and other contributors + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp index 822e361..879dcb2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp @@ -39,6 +39,7 @@ <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/angular/angular.min.js"></script> <script type="text/javascript" src="assets/angular-messages/angular-messages.min.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index cee2c5e..4fde541 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -62,6 +62,7 @@ <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script> <script type="text/javascript" src="assets/jquery-minicolors/jquery.minicolors.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> @@ -130,6 +131,7 @@ <div id="port-tooltips"></div> <div id="process-group-tooltips"></div> <div id="remote-process-group-tooltips"></div> + <div id="connection-tooltips"></div> </div> <jsp:include page="/WEB-INF/partials/canvas/navigation.jsp"/> <jsp:include page="/WEB-INF/partials/canvas/settings-content.jsp"/> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp index 61aa5ca..eb8b0da 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp @@ -43,6 +43,7 @@ <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp index 8c276e3..239f6a6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp @@ -41,6 +41,7 @@ <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp index 429eaf3..47700df 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp @@ -41,6 +41,7 @@ <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp index 6efffdc..1204e19 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp @@ -40,6 +40,7 @@ <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> ${nf.login.script.tags} </head> <body class="login-body"> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp index 18fa0c9..9862a59 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp @@ -45,6 +45,7 @@ <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp index 394f630..5d6ad09 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp @@ -50,6 +50,7 @@ <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script> <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp index f176d03..68b95d9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp @@ -41,6 +41,7 @@ <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp index 667f2ef..4295fdf 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp @@ -43,6 +43,7 @@ <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script> <script type="text/javascript" src="js/jquery/jquery.each.js"></script> <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script> + <script type="text/javascript" src="assets/moment/min/moment.min.js"></script> <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script> <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script> <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css index 4c9f289..f47771b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css @@ -344,7 +344,24 @@ text.connection-from-run-status.is-missing-port, text.connection-to-run-status.i } g.connection rect.backpressure-tick { - fill: #3e3e3e; + fill: transparent; +} + +g.connection rect.backpressure-tick.data-size-prediction.prediction-down, +g.connection rect.backpressure-tick.object-prediction.prediction-down { + fill: white; +} + +g.connection rect.backpressure-tick.data-size-prediction, +g.connection rect.backpressure-tick.object-prediction { + fill: black; +} + +g.connection rect.backpressure-tick.data-size-prediction.not-configured, +g.connection rect.backpressure-tick.object-prediction.not-configured, +g.connection rect.backpressure-tick.data-size-prediction.prediction-down.not-configured, +g.connection rect.backpressure-tick.object-prediction.prediction-down.not-configured { + fill: transparent; } g.connection rect.backpressure-tick.not-configured { @@ -355,6 +372,12 @@ g.connection rect.backpressure-object, g.connection rect.backpressure-data-size fill: #d8d8d8; } +/**/ +g.connection rect.backpressure-object>title>tspan, g.connection rect.backpressure-data-size>title>tspan { + display: block; +} +/**/ + g.connection rect.backpressure-object.not-configured, g.connection rect.backpressure-data-size.not-configured { fill: transparent; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js index b325c32..2a50f4c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js @@ -26,9 +26,10 @@ 'nf.Storage', 'nf.ErrorHandler', 'nf.Client', - 'nf.CanvasUtils'], - function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils) { - return (nf.Connection = factory($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils)); + 'nf.CanvasUtils', + 'lodash-core'], + function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _) { + return (nf.Connection = factory($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.Connection = @@ -39,7 +40,8 @@ require('nf.Storage'), require('nf.ErrorHandler'), require('nf.Client'), - require('nf.CanvasUtils'))); + require('nf.CanvasUtils'), + require('lodash-code'))); } else { nf.Connection = factory(root.$, root.d3, @@ -48,9 +50,10 @@ root.nf.Storage, root.nf.ErrorHandler, root.nf.Client, - root.nf.CanvasUtils); + root.nf.CanvasUtils, + root._); } -}(this, function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils) { +}(this, function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _) { 'use strict'; var nfSelectable; @@ -66,6 +69,9 @@ // width of a backpressure indicator - half of width, left/right padding, left/right border var backpressureBarWidth = (dimensions.width / 2) - 15 - 2; + var backpressureCountOffset = 6; + var backpressureDataSizeOffset = (dimensions.width / 2) + 10 + 1; + // -------------------------- // Snap alignment for drag events // -------------------------- @@ -1238,92 +1244,121 @@ var yBackpressureOffset = rowHeight + HEIGHT_FOR_BACKPRESSURE - 4; // backpressure object threshold + var backpressureObjectContainer = queued.append('g') + .attrs({ + 'transform': 'translate(' + backpressureCountOffset + ', ' + yBackpressureOffset + ')', + 'class': 'backpressure-object-container' + }); // start - queued.append('rect') + backpressureObjectContainer.append('rect') .attrs({ 'class': 'backpressure-tick object', 'width': 1, 'height': 3, - 'x': 5, - 'y': yBackpressureOffset + 'x': 0, + 'y': 0 }); // bar - var backpressureCountOffset = 6; - queued.append('rect') + backpressureObjectContainer.append('rect') .attrs({ 'class': 'backpressure-object', 'width': backpressureBarWidth, 'height': 3, - 'x': backpressureCountOffset, - 'y': yBackpressureOffset - }) - .append('title'); + 'x': 0, + 'y': 0 + }); // end - queued.append('rect') + backpressureObjectContainer.append('rect') .attrs({ 'class': 'backpressure-tick object', 'width': 1, 'height': 3, - 'x': backpressureCountOffset + backpressureBarWidth, - 'y': yBackpressureOffset + 'x': backpressureBarWidth, + 'y': 0 }); // percent full - queued.append('rect') + backpressureObjectContainer.append('rect') .attrs({ 'class': 'backpressure-percent object', 'width': 0, 'height': 3, - 'x': backpressureCountOffset, - 'y': yBackpressureOffset + 'x': 0, + 'y': 0 + }); + + // prediction indicator + backpressureObjectContainer.append('rect') + .attrs({ + 'class': 'backpressure-tick object-prediction', + 'width': 1, + 'height': 3, + 'x': backpressureBarWidth, + 'y': 0 }); // backpressure data size threshold + var backpressureDataSizeContainer = queued.append('g') + .attrs({ + 'transform': 'translate(' + backpressureDataSizeOffset + ', ' + yBackpressureOffset + ')', + 'class': 'backpressure-data-size-container' + }); + // start - queued.append('rect') + backpressureDataSizeContainer.append('rect') .attrs({ 'class': 'backpressure-tick data-size', 'width': 1, 'height': 3, - 'x': (dimensions.width / 2) + 10, - 'y': yBackpressureOffset + 'x': 0, + 'y': 0 }); // bar - var backpressureDataSizeOffset = (dimensions.width / 2) + 10 + 1; - queued.append('rect') + backpressureDataSizeContainer.append('rect') .attrs({ 'class': 'backpressure-data-size', 'width': backpressureBarWidth, 'height': 3, - 'x': backpressureDataSizeOffset, - 'y': yBackpressureOffset + 'x': 0, + 'y': 0 }) .append('title'); // end - queued.append('rect') + backpressureDataSizeContainer.append('rect') .attrs({ 'class': 'backpressure-tick data-size', 'width': 1, 'height': 3, - 'x': backpressureDataSizeOffset + backpressureBarWidth, - 'y': yBackpressureOffset + 'x': backpressureBarWidth, + 'y': 0 }); // percent full - queued.append('rect') + backpressureDataSizeContainer.append('rect') .attrs({ 'class': 'backpressure-percent data-size', 'width': 0, 'height': 3, - 'x': backpressureDataSizeOffset, - 'y': yBackpressureOffset + 'x': 0, + 'y': 0 }); + + // prediction indicator + backpressureDataSizeContainer.append('rect') + .attrs({ + 'class': 'backpressure-tick data-size-prediction', + 'width': 1, + 'height': 3, + 'x': backpressureBarWidth, + 'y': 0 + }); + } else { backgrounds.push(queued.select('rect.connection-label-background')); borders.push(queued.select('rect.connection-label-border')); @@ -1441,6 +1476,10 @@ .classed('not-configured', function () { return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseCount); }); + connectionLabelContainer.selectAll('rect.backpressure-tick.object-prediction') + .classed('not-configured', function () { + return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseCount); + }); // update backpressure data size fill connectionLabelContainer.select('rect.backpressure-data-size') @@ -1451,6 +1490,10 @@ .classed('not-configured', function () { return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseBytes); }); + connectionLabelContainer.selectAll('rect.backpressure-tick.data-size-prediction') + .classed('not-configured', function () { + return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseBytes); + }); if (d.permissions.canWrite) { // only support dragging the label when appropriate @@ -1476,6 +1519,95 @@ }); }; + var isAtBackPressure = function (d) { + var percentUseCount = _.get(d, 'status.aggregateSnapshot.percentUseCount', 0); + var percentUseBytes = _.get(d, 'status.aggregateSnapshot.percentUseBytes', 0); + return Math.max(percentUseCount, percentUseBytes) >= 100; + }; + + /** + * Gets the tooltip content for the back pressure count metric + * @param d + */ + var getBackPressureCountTip = function (d) { + var tooltipContent; + var percentUseCount = _.get(d, 'status.aggregateSnapshot.percentUseCount'); + if (_.isNumber(percentUseCount)) { + var objectThreshold = _.get(d, 'component.backPressureObjectThreshold'); + + var predictedPercentCount = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', -1); + var timeToBackPressure = _.get(d, 'status.aggregateSnapshot.predictedMillisUntilCountBackpressure', -1); + + var tooltipLines = ['Queue: ' + _.clamp(percentUseCount, 0, 100) + '% full (based on ' + objectThreshold + ' object threshold)']; + + // only show predicted percent if it is non-negative + if (_.isNumber(predictedPercentCount) && predictedPercentCount > -1) { + var predictionIntervalSeconds = _.get(d, 'status.aggregateSnapshot.predictionIntervalSeconds', 60 * 5); + tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): ' + _.clamp(predictedPercentCount, 0, 100) + '%') + } + + // only show an estimate if it is valid (non-negative but less than the max number supported) + if (_.isNumber(timeToBackPressure) && _.inRange(timeToBackPressure, 0, Number.MAX_SAFE_INTEGER) && !isAtBackPressure(d)) { + var duration = nfCommon.formatPredictedDuration(timeToBackPressure); + tooltipLines.push('Estimated time to back pressure: ' + duration); + } + + if (_.isEmpty(tooltipLines)) { + return ''; + } else if (_.size(tooltipLines) === 1) { + return tooltipLines[0]; + } else { + tooltipContent = nfCommon.formatUnorderedList(tooltipLines) + } + } else { + tooltipContent = 'Back Pressure Object Threshold is not configured'; + } + + return tooltipContent; + }; + + /** + * Gets the tooltip content for the back pressure size metric + * @param d + */ + var getBackPressureSizeTip = function (d) { + var tooltipContent; + var percentUseBytes = _.get(d, 'status.aggregateSnapshot.percentUseBytes'); + + if (_.isNumber(percentUseBytes)) { + var dataSizeThreshold = _.get(d, 'component.backPressureDataSizeThreshold'); + + var predictedPercentBytes = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', -1); + var timeToBackPressure = _.get(d, 'status.aggregateSnapshot.predictedMillisUntilBytesBackpressure', -1); + + var tooltipLines = ['Queue: ' + _.clamp(percentUseBytes, 0, 100) + '% full (based on ' + dataSizeThreshold + ' data size threshold)']; + + // only show predicted percent if it is non-negative + if (_.isNumber(predictedPercentBytes) && predictedPercentBytes > -1) { + var predictionIntervalSeconds = _.get(d, 'status.aggregateSnapshot.predictionIntervalSeconds', 60 * 5); + tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): ' + _.clamp(predictedPercentBytes, 0, 100) + '%') + } + + // only show an estimate if it is valid (non-negative but less than the max number supported) + if (_.isNumber(timeToBackPressure) && _.inRange(timeToBackPressure, 0, Number.MAX_SAFE_INTEGER) && !isAtBackPressure(d)) { + var duration = nfCommon.formatPredictedDuration(timeToBackPressure); + tooltipLines.push('Estimated time to back pressure: ' + duration); + } + + if (_.isEmpty(tooltipLines)) { + return ''; + } else if (_.size(tooltipLines) === 1) { + return tooltipLines[0]; + } else { + tooltipContent = nfCommon.formatUnorderedList(tooltipLines) + } + } else { + tooltipContent = 'Back Pressure Data Size Threshold is not configured'; + } + + return tooltipContent; + }; + /** * Updates the stats of the connections in the specified selection. * @@ -1517,13 +1649,53 @@ deferred.resolve(); }); - updated.select('rect.backpressure-data-size').select('title').text(function (d) { - if (nfCommon.isDefinedAndNotNull(d.status.aggregateSnapshot.percentUseBytes)) { - return 'Queue is ' + d.status.aggregateSnapshot.percentUseBytes + '% full based on Back Pressure Data Size Threshold'; - } else { - return 'Back Pressure Data Size Threshold is not configured'; - } + var backpressurePercentDataSizePrediction = updated.select('rect.backpressure-tick.data-size-prediction'); + backpressurePercentDataSizePrediction.transition() + .duration(400) + .attrs({ + 'x': function (d) { + // clamp the prediction between 0 and 100 percent + var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', 0); + return (backpressureBarWidth * _.clamp(predicted, 0, 100)) / 100; + }, + 'display': function (d) { + var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', -1); + if (predicted >= 0) { + return 'unset'; + } else { + // don't show it if there not a valid prediction + return 'none'; + } + } + }).on('end', function () { + backpressurePercentDataSizePrediction.classed('prediction-down', function (d) { + var actual = _.get(d, 'status.aggregateSnapshot.percentUseBytes', 0); + var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', 0); + return predicted < actual; + }) }); + + updated.select('g.backpressure-data-size-container') + .each(function(d) { + var tip = d3.select('#back-pressure-size-tip-' + d.id); + + // create a DOM element for the tooltip if ones does not already exist + if (tip.empty()) { + tip = d3.select('#connection-tooltips') + .append('div') + .attr('id', function() { + return 'back-pressure-size-tip-' + d.id + }) + .attr('class', 'tooltip nifi-tooltip'); + } + + // update the tooltip + tip.html(function() { + return $('<div></div>').append(getBackPressureSizeTip(d)).html(); + }); + + nfCanvasUtils.canvasTooltip(tip, d3.select(this)); + }); }).promise(); // update object count @@ -1546,24 +1718,65 @@ } } }).on('end', function () { - backpressurePercentObject - .classed('warning', function (d) { - return isWarningCount(d); + backpressurePercentObject + .classed('warning', function (d) { + return isWarningCount(d); + }) + .classed('error', function (d) { + return isErrorCount(d); + }); + + deferred.resolve(); + }); + + + var backpressurePercentObjectPrediction = updated.select('rect.backpressure-tick.object-prediction'); + backpressurePercentObjectPrediction.transition() + .duration(400) + .attrs({ + 'x': function (d) { + // clamp the prediction between 0 and 100 percent + var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', 0); + return (backpressureBarWidth * _.clamp(predicted, 0, 100)) / 100; + }, + 'display': function (d) { + var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', -1); + if (predicted >= 0) { + return 'unset'; + } else { + // don't show it if there not a valid prediction + return 'none'; + } + } + }).on('end', function () { + backpressurePercentObjectPrediction.classed('prediction-down', function (d) { + var actual = _.get(d, 'status.aggregateSnapshot.percentUseCount', 0); + var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', 0); + return predicted < actual; }) - .classed('error', function (d) { - return isErrorCount(d); - }); + }); - deferred.resolve(); - }); + updated.select('g.backpressure-object-container') + .each(function(d) { + var tip = d3.select('#back-pressure-count-tip-' + d.id); - updated.select('rect.backpressure-object').select('title').text(function (d) { - if (nfCommon.isDefinedAndNotNull(d.status.aggregateSnapshot.percentUseCount)) { - return 'Queue is ' + d.status.aggregateSnapshot.percentUseCount + '% full based on Back Pressure Object Threshold'; - } else { - return 'Back Pressure Object Threshold is not configured'; - } - }); + // create a DOM element for the tooltip if ones does not already exist + if (tip.empty()) { + tip = d3.select('#connection-tooltips') + .append('div') + .attr('id', function() { + return 'back-pressure-count-tip-' + d.id + }) + .attr('class', 'tooltip nifi-tooltip'); + } + + // update the tooltip + tip.html(function() { + return $('<div></div>').append(getBackPressureCountTip(d)).html(); + }); + + nfCanvasUtils.canvasTooltip(tip, d3.select(this)); + }); }).promise(); // update connection once progress bars have transitioned @@ -1627,7 +1840,15 @@ }); // remove the connection - removed.remove(); + removed.call(removeTooltips).remove(); + }; + + var removeTooltips = function (removed) { + removed.each(function (d) { + // remove any associated tooltips + $('#back-pressure-size-tip-' + d.id).remove(); + $('#back-pressure-count-tip-' + d.id).remove(); + }); }; var nfConnection = { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 7e5a1c0..13ee565 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -23,22 +23,25 @@ define(['jquery', 'd3', 'nf.Storage', - 'lodash-core'], - function ($, d3, nfStorage, _) { - return (nf.Common = factory($, d3, nfStorage, _)); + 'lodash-core', + 'moment'], + function ($, d3, nfStorage, _, moment) { + return (nf.Common = factory($, d3, nfStorage, _, moment)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.Common = factory(require('jquery'), require('d3'), require('nf.Storage'), - require('lodash-core'))); + require('lodash-core'), + require('moment'))); } else { nf.Common = factory(root.$, root.d3, root.nf.Storage, - root._); + root._, + root.moment); } -}(this, function ($, d3, nfStorage, _) { +}(this, function ($, d3, nfStorage, _, moment) { 'use strict'; $(document).ready(function () { @@ -1278,6 +1281,13 @@ } }, + formatPredictedDuration: function (duration) { + if (duration === 0) { + return 'now'; + } + return moment.duration(duration, 'ms').humanize(); + }, + /** * Constants for formatting data size. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js index 3f2fd1d..4f02e74 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js @@ -25,9 +25,10 @@ 'nf.StatusHistory', 'nf.ProcessorDetails', 'nf.ConnectionDetails', - 'nf.ng.Bridge'], - function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge) { - return (nf.SummaryTable = factory($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge)); + 'nf.ng.Bridge', + 'lodash-core'], + function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge, _) { + return (nf.SummaryTable = factory($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge, _)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.SummaryTable = @@ -38,7 +39,8 @@ require('nf.StatusHistory'), require('nf.ProcessorDetails'), require('nf.ConnectionDetails'), - require('nf.ng.Bridge'))); + require('nf.ng.Bridge'), + require('lodash-core'))); } else { nf.SummaryTable = factory(root.$, root.Slick, @@ -47,9 +49,10 @@ root.nf.StatusHistory, root.nf.ProcessorDetails, root.nf.ConnectionDetails, - root.nf.ng.Bridge); + root.nf.ng.Bridge, + root._); } -}(this, function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge) { +}(this, function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge, _) { 'use strict'; /** @@ -64,6 +67,8 @@ } }; + var DATA_SEPARATOR = ' | '; + /** * Goes to the specified component if possible. * @@ -291,12 +296,12 @@ // formatter for io var ioFormatter = function (row, cell, value, columnDef, dataContext) { - return nfCommon.escapeHtml(dataContext.read) + ' / ' + nfCommon.escapeHtml(dataContext.written); + return nfCommon.escapeHtml(dataContext.read) + DATA_SEPARATOR + nfCommon.escapeHtml(dataContext.written); }; // formatter for tasks var taskTimeFormatter = function (row, cell, value, columnDef, dataContext) { - return nfCommon.formatInteger(dataContext.tasks) + ' / ' + nfCommon.escapeHtml(dataContext.tasksDuration); + return nfCommon.formatInteger(dataContext.tasks) + DATA_SEPARATOR + nfCommon.escapeHtml(dataContext.tasksDuration); }; // function for formatting the last accessed time @@ -309,7 +314,7 @@ var threadCounts = ''; var threadTip = ''; if (dataContext.terminatedThreadCount > 0) { - threadCounts = '(' + dataContext.activeThreadCount + ' / ' + dataContext.terminatedThreadCount + ')'; + threadCounts = '(' + dataContext.activeThreadCount + DATA_SEPARATOR + dataContext.terminatedThreadCount + ')'; threadTip = 'Threads: (Active / Terminated)'; } else if (dataContext.activeThreadCount > 0) { threadCounts = '(' + dataContext.activeThreadCount + ')'; @@ -372,7 +377,7 @@ var inputColumn = { id: 'input', field: 'input', - name: '<span class="input-title">In</span> / <span class="input-size-title">Size</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="input-title">In</span> (<span class="input-size-title">Size</span>) <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Count / data size in the last 5 min', sortable: true, defaultSortAsc: false, @@ -382,7 +387,7 @@ var ioColumn = { id: 'io', field: 'io', - name: '<span class="read-title">Read</span> / <span class="written-title">Write</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="read-title">Read</span>' + DATA_SEPARATOR + '<span class="written-title">Write</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Data size in the last 5 min', formatter: ioFormatter, sortable: true, @@ -392,7 +397,7 @@ var outputColumn = { id: 'output', field: 'output', - name: '<span class="output-title">Out</span> / <span class="output-size-title">Size</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="output-title">Out</span> (<span class="output-size-title">Size</span>) <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Count / data size in the last 5 min', sortable: true, defaultSortAsc: false, @@ -402,7 +407,7 @@ var tasksTimeColumn = { id: 'tasks', field: 'tasks', - name: '<span class="tasks-title">Tasks</span> / <span class="time-title">Time</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="tasks-title">Tasks</span>' + DATA_SEPARATOR + '<span class="time-title">Time</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Count / duration in the last 5 min', formatter: taskTimeFormatter, sortable: true, @@ -708,23 +713,58 @@ return '<div class="pointer show-connection-details fa fa-info-circle" title="View Connection Details"></div>'; }; + var formatPercent = function (value) { + return _.isNumber(value) && value >= 0 ? _.clamp(value, 0, 100) + '%' : 'NA'; + }; + var backpressureFormatter = function (row, cell, value, columnDef, dataContext) { var percentUseCount = 'NA'; if (nfCommon.isDefinedAndNotNull(dataContext.percentUseCount)) { - percentUseCount = dataContext.percentUseCount + '%'; + percentUseCount = formatPercent(dataContext.percentUseCount); } var percentUseBytes = 'NA'; if (nfCommon.isDefinedAndNotNull(dataContext.percentUseBytes)) { - percentUseBytes = dataContext.percentUseBytes + '%'; + percentUseBytes = formatPercent(dataContext.percentUseBytes); + } + return nfCommon.escapeHtml(percentUseCount) + DATA_SEPARATOR + nfCommon.escapeHtml(percentUseBytes); + }; + + var backpressurePredictionFormatter = function (row, cell, value, columnDef, dataContext) { + var predictedMillisUntilBytesBackpressure = _.get(dataContext, 'predictedMillisUntilBytesBackpressure', -1); + var predictedMillisUntilCountBackpressure = _.get(dataContext, 'predictedMillisUntilCountBackpressure', -1); + + var percentUseCount = _.get(dataContext, 'percentUseCount', 0); + var percentUseBytes = _.get(dataContext, 'percentUseBytes', 0); + + var predictions = [ + { label: 'object', timeToBackPressure: predictedMillisUntilCountBackpressure }, + { label: 'size', timeToBackPressure: predictedMillisUntilBytesBackpressure }, + ]; + var actualQueuePercents = [ + { label: 'object', percent: percentUseCount }, + { label: 'size', percent: percentUseBytes } + ]; + + var minPrediction = _.minBy(predictions, 'timeToBackPressure'); + var maxActual = _.maxBy(actualQueuePercents, 'percent'); + + if (maxActual.percent >= 100) { + // currently experiencing back pressure + return 'now (' + maxActual.label + ')'; + } else if (minPrediction.timeToBackPressure < 0) { + // there is not a valid time-to-back-pressure prediction available + return 'NA'; } - return nfCommon.escapeHtml(percentUseCount) + ' / ' + nfCommon.escapeHtml(percentUseBytes); + + var formatted = nfCommon.formatPredictedDuration(minPrediction.timeToBackPressure); + return nfCommon.escapeHtml(formatted) + ' (' + minPrediction.label + ')'; }; // define the input, read, written, and output columns (reused between both tables) var queueColumn = { id: 'queued', field: 'queued', - name: '<span class="queued-title">Queue</span> / <span class="queued-size-title">Size</span>', + name: '<span class="queued-title">Queue</span> (<span class="queued-size-title">Size</span>)', sortable: true, defaultSortAsc: false, resize: true, @@ -735,13 +775,25 @@ var backpressureColumn = { id: 'backpressure', field: 'backpressure', - name: '<span class="backpressure-object-title">Queue</span> / <span class="backpressure-data-size-title">Size</span> Threshold', + name: 'Threshold %: <span class="backpressure-object-title">Queue</span> | <span class="backpressure-data-size-title">Size</span>', sortable: true, defaultSortAsc: false, formatter: backpressureFormatter, resize: true }; + // define the column used to display backpressure predicted values (reused in both tables) + var backpressurePredictionColumn = { + id: 'backpressurePrediction', + field: 'backpressurePrediction', + name: 'Estimated Time to Back Pressure', + sortable: true, + defaultSortAsc: false, + formatter: backpressurePredictionFormatter, + resize: true, + toolTip: 'Estimated Time to Back Pressure' + }; + // define the column model for the summary table var connectionsColumnModel = [ { @@ -754,14 +806,6 @@ maxWidth: 50 }, { - id: 'sourceName', - field: 'sourceName', - name: 'Source Name', - sortable: true, - resizable: true, - formatter: nfCommon.genericValueFormatter - }, - { id: 'name', field: 'name', name: 'Name', @@ -769,18 +813,27 @@ resizable: true, formatter: valueFormatter }, + queueColumn, + backpressureColumn, + backpressurePredictionColumn, + inputColumn, + { + id: 'sourceName', + field: 'sourceName', + name: 'From Source', + sortable: true, + resizable: true, + formatter: nfCommon.genericValueFormatter + }, + outputColumn, { id: 'destinationName', field: 'destinationName', - name: 'Destination Name', + name: 'To Destination', sortable: true, resizable: true, formatter: nfCommon.genericValueFormatter - }, - inputColumn, - queueColumn, - backpressureColumn, - outputColumn + } ]; // add an action column if appropriate @@ -952,9 +1005,10 @@ resizable: true, formatter: nfCommon.genericValueFormatter }, - inputColumn, queueColumn, backpressureColumn, + backpressurePredictionColumn, + inputColumn, outputColumn ]; @@ -1032,7 +1086,7 @@ var transferredColumn = { id: 'transferred', field: 'transferred', - name: '<span class="transferred-title">Transferred</span> / <span class="transferred-size-title">Size</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="transferred-title">Transferred</span> (<span class="transferred-size-title">Size</span>) <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Count / data size transferred to and from connections in the last 5 min', resizable: true, defaultSortAsc: false, @@ -1042,7 +1096,7 @@ var sentColumn = { id: 'sent', field: 'sent', - name: '<span class="sent-title">Sent</span> / <span class="sent-size-title">Size</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="sent-title">Sent</span> (<span class="sent-size-title">Size</span>) <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Count / data size in the last 5 min', sortable: true, defaultSortAsc: false, @@ -1052,7 +1106,7 @@ var receivedColumn = { id: 'received', field: 'received', - name: '<span class="received-title">Received</span> / <span class="received-size-title">Size</span> <span style="font-weight: normal; overflow: hidden;">5 min</span>', + name: '<span class="received-title">Received</span> (<span class="received-size-title">Size</span>) <span style="font-weight: normal; overflow: hidden;">5 min</span>', toolTip: 'Count / data size in the last 5 min', sortable: true, defaultSortAsc: false, @@ -2296,6 +2350,10 @@ var bPercentUseDataSize = nfCommon.isDefinedAndNotNull(b['percentUseBytes']) ? b['percentUseBytes'] : -1; return aPercentUseDataSize - bPercentUseDataSize; } + } else if (sortDetails.columnId === 'backpressurePrediction') { + var aMinTime = Math.min(_.get(a, 'predictedMillisUntilBytesBackpressure', Number.MAX_VALUE), _.get(a, 'predictedMillisUntilCountBackpressure', Number.MAX_VALUE)); + var bMinTime = Math.min(_.get(b, 'predictedMillisUntilBytesBackpressure', Number.MAX_VALUE), _.get(b, 'predictedMillisUntilCountBackpressure', Number.MAX_VALUE)); + return aMinTime - bMinTime; } else if (sortDetails.columnId === 'sent' || sortDetails.columnId === 'received' || sortDetails.columnId === 'input' || sortDetails.columnId === 'output' || sortDetails.columnId === 'transferred') { var aSplit = a[sortDetails.columnId].split(/\(([^)]+)\)/); var bSplit = b[sortDetails.columnId].split(/\(([^)]+)\)/); @@ -2349,6 +2407,9 @@ $('#' + tableId + ' span.queued-size-title').removeClass('sorted'); $('#' + tableId + ' span.backpressure-object-title').removeClass('sorted'); $('#' + tableId + ' span.backpressure-data-size-title').removeClass('sorted'); + $('#' + tableId + ' span.backpressure-prediction-object-title').removeClass('sorted'); + $('#' + tableId + ' span.backpressure-prediction-data-size-title').removeClass('sorted'); + $('#' + tableId + ' span.backpressure-prediction-time-title').removeClass('sorted'); $('#' + tableId + ' span.input-title').removeClass('sorted'); $('#' + tableId + ' span.input-size-title').removeClass('sorted'); $('#' + tableId + ' span.output-title').removeClass('sorted'); @@ -2776,6 +2837,13 @@ queuedSize: snapshot.queuedSize, percentUseCount: snapshot.percentUseCount, percentUseBytes: snapshot.percentUseBytes, + predictedPercentBytes: snapshot.predictedPercentBytes, + predictedPercentCount: snapshot.predictedPercentCount, + predictedBytesAtNextInterval: snapshot.predictedBytesAtNextInterval, + predictedCountAtNextInterval: snapshot.predictedCountAtNextInterval, + predictedMillisUntilBytesBackpressure: snapshot.predictedMillisUntilBytesBackpressure, + predictedMillisUntilCountBackpressure: snapshot.predictedMillisUntilCountBackpressure, + predictionIntervalSeconds: snapshot.predictionIntervalSeconds, output: snapshot.output }); });
