This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git

commit 22baa3c81716e6a2990884a5b0faccfa17b98fae
Author: Alex Heneveld <[email protected]>
AuthorDate: Mon Aug 14 23:13:57 2023 +0100

    improve sensors page displaying large data objects
---
 .../config-sensor-table.directive.js               | 68 +++++++++++++++-------
 .../config-sensor-table.template.html              |  8 +--
 ui-modules/app-inspector/app/index.js              |  1 +
 3 files changed, 52 insertions(+), 25 deletions(-)

diff --git 
a/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.directive.js
 
b/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.directive.js
index c43d9812..448de0cd 100644
--- 
a/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.directive.js
+++ 
b/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.directive.js
@@ -46,37 +46,60 @@ export function configSensorTableDirective(brSnackbar) {
 
     function link(scope) {
         scope.items = [];
-        scope.mapInfo = {};
+        scope.itemsCache = {};
         scope.WARNING_TEXT = 'This value is identified as potentially 
sensitive based and so is masked here. ' +
             'The value should be supplied as a DSL expression not as plain 
text. ' +
             'Note that the unmasked value still might not reveal the actual 
value, ' +
             'if sensitive values are blocked by the API or if DSL resolution 
is skipped.';
 
-        scope.$watchGroup(['data'], (changes)=> {
-            if (angular.isObject(scope.data)) {
+        function dumpAndTruncate(x) {
+            let result = jsyaml.dump(x);
+            if (result && result.length > 100000) {
+                result = result.slice(0, 100000) + '\n...';
+            }
+            return result;
+        }
+
+        function update() {
+            if (angular.isObject(scope.data) && scope.mapInfo) {
                 const dataPlusReconfigurable = Object.assign({}, scope.data);
-                if (scope.info) {
-                    scope.info.forEach(info => {
-                        if (info.reconfigurable && 
!dataPlusReconfigurable.hasOwnProperty(info.name)) {
-                            dataPlusReconfigurable[info.name] = undefined;
+                scope.info.forEach(info => {
+                    if (info.reconfigurable && 
!dataPlusReconfigurable.hasOwnProperty(info.name)) {
+                        dataPlusReconfigurable[info.name] = undefined;
+                    }
+                });
+                scope.items = Object.entries(dataPlusReconfigurable)
+                    .map(([key, value]) => {
+                        const old = scope.itemsCache[key];
+                        if (old && _.isEqual(value, old.value)) {
+                            return old;
                         }
+                        const isObject = angular.isObject(value);
+                        scope.mapInfo[key] = Object.assign({isObject}, 
scope.mapInfo[key]);
+                        // minimize calls to yamlification; can take 100ms+ 
for large objects
+                        // (and no nice way i can see to reduce that or to 
background it)
+                        let valueDumped = isObject ? 
_.escape(dumpAndTruncate(value)) : value;
+                        const result = {
+                            key,
+                            value,
+                            valueDumped,
+                            isPlaintextSensitiveValue: 
scope.checkPlaintextSensitiveKeyValue && 
scope.checkPlaintextSensitiveKeyValue(key, value),
+                        };
+                        scope.itemsCache[key] = result;
+                        return result;
                     });
-                }
-                scope.items = Object.entries(dataPlusReconfigurable)
-                    .map(([key, value]) => ({
-                        key,
-                        value,
-                        isPlaintextSensitiveValue: 
scope.checkPlaintextSensitiveKeyValue && 
scope.checkPlaintextSensitiveKeyValue(key, value),
-                    }));
             }
-        });
+        }
 
+        scope.$watchGroup(['data'], (changes)=> { update(); });
         scope.$watch('info', () => {
             if (angular.isArray(scope.info)) {
+                if (!scope.mapInfo) scope.mapInfo = {};
                 scope.mapInfo = scope.info.reduce((pool, infoItem) => {
-                    pool[infoItem.name] = infoItem;
+                    pool[infoItem.name] = Object.assign({}, 
scope.mapInfo[infoItem.name], infoItem);
                     return pool;
                 }, {});
+                update();
             }
         });
 
@@ -102,8 +125,8 @@ export function configSensorTableDirective(brSnackbar) {
     }
 }
 
-function asJsonIfJson(input, knownJson, $sanitize) {
-    if (!knownJson) {
+function asJsonIfJson(input, isKnownString, isYamledObject, $sanitize) {
+    if (isKnownString) {
         if (!input) return null;
         let inputTrimmed = input.trim();
         if ((inputTrimmed.startsWith("{") && inputTrimmed.endsWith("}")) || 
(inputTrimmed.startsWith("[") && inputTrimmed.endsWith("]"))) {
@@ -113,7 +136,8 @@ function asJsonIfJson(input, knownJson, $sanitize) {
             return null;
         }
     }
-    return $sanitize('<div class="multiline-code">' + 
_.escape(jsyaml.dump(input)) + '</div>');
+    const yamld = isYamledObject ? input : _.escape(jsyaml.dump(input));
+    return $sanitize('<div class="multiline-code">' + yamld + '</div>');
 }
 
 export function brLinkyFilter($filter, $state, $sanitize) {
@@ -121,8 +145,10 @@ export function brLinkyFilter($filter, $state, $sanitize) {
     return function(input, key, target, attributes) {
         if (input == null) {
             return '';
+        } else if (angular.isObject(key) && key.isObject) {
+            return asJsonIfJson(input, false, true, $sanitize) || 
$filter('linky')(angular.toJson(input), target, attributes);
         } else if (!angular.isString(input)) {
-            return asJsonIfJson(input, true, $sanitize) || 
$filter('linky')(angular.toJson(input), target, attributes);
+            return asJsonIfJson(input, false, false, $sanitize) || 
$filter('linky')(angular.toJson(input), target, attributes);
         } else if (angular.isObject(key) && angular.isString(key.name) && 
(key.name.indexOf('ssh') > -1 || isSensitiveFieldName(key.name))) {
             return input;
         } else if (angular.isObject(key) && key.links && 
key.links.hasOwnProperty('action:open')) {
@@ -131,7 +157,7 @@ export function brLinkyFilter($filter, $state, $sanitize) {
                 $sanitize('<a href="' + $state.href('main.inspect.summary', 
{applicationId: matches[1], entityId: matches[2]}) + '">' + input + '</a>') :
                 $filter('linky')(input, target, attributes);
         } else {
-            return asJsonIfJson(input, false, $sanitize) || 
$filter('linky')(input, target, attributes);
+            return asJsonIfJson(input, true, false, $sanitize) || 
$filter('linky')(input, target, attributes);
         }
     }
 }
diff --git 
a/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.template.html
 
b/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.template.html
index ceca5f34..4e179a11 100644
--- 
a/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.template.html
+++ 
b/ui-modules/app-inspector/app/components/config-sensor-table/config-sensor-table.template.html
@@ -36,14 +36,14 @@
     <div class="col2 config-value" ng-class="{ 'grid-even': $index%2 }" 
ng-repeat-end>
               <div class="content-row" sensitive-field 
field-name="{{item.key}}">
                 <span class="content">
-                    <div ng-show="mapInfo[item.key].reconfigurable" 
ng-class="{'grid-editable': mapInfo[item.key].reconfigurable}" 
editable-text="item.value"
+                    <div ng-if="mapInfo[item.key].reconfigurable" 
ng-class="{'grid-editable': mapInfo[item.key].reconfigurable}" 
editable-text="item.value"
                          edit-disabled="!mapInfo[item.key].reconfigurable" 
onbeforesave="reconfigureCallback(item.key,$data)" buttons="right" 
e-disabled-submit-on-enter>
                             <span ng-if="isNullish(item.value)"><i>value not 
set</i></span>
-                            <span ng-if="!isNullish(item.value)" 
ng-bind-html="item.value | brLinky:mapInfo[item.key]" 
class="literal-lines"></span>
+                            <span ng-if="!isNullish(item.value)" 
ng-bind-html="item.valueDumped | brLinky:mapInfo[item.key]" 
class="literal-lines"></span>
                     </div>
-                    <div ng-show="!mapInfo[item.key].reconfigurable">
+                    <div ng-if="!mapInfo[item.key].reconfigurable">
                             <span ng-if="isNullish(item.value)"><i>value not 
set</i></span>
-                            <span ng-if="!isNullish(item.value)" 
ng-bind-html="item.value | brLinky:mapInfo[item.key]" 
class="literal-lines"></span>
+                            <span ng-if="!isNullish(item.value)" 
ng-bind-html="item.valueDumped | brLinky:mapInfo[item.key]" 
class="literal-lines"></span>
                     </div>
                 </span>
                 <span class="extras" onclick="event.stopPropagation();">
diff --git a/ui-modules/app-inspector/app/index.js 
b/ui-modules/app-inspector/app/index.js
index 37e274ec..00848b22 100755
--- a/ui-modules/app-inspector/app/index.js
+++ b/ui-modules/app-inspector/app/index.js
@@ -97,6 +97,7 @@ function applicationConfig($urlRouterProvider, 
$stateProvider, $logProvider, $co
         .state(detailState)
         .state(streamState);
     $httpProvider.interceptors.push('apiObserverInterceptor');
+    // $httpProvider.useApplyAsync(true);  // could be useful if making many 
small calls
     apiObserverInterceptorProvider.interval(5000);
 }
 

Reply via email to