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); }
