You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2023/08/14 22:42:08 UTC
[brooklyn-ui] 02/02: improve sensors page displaying large data objects
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 <al...@cloudsoft.io>
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);
}