[pbs-devel] [PATCH proxmox-widget-toolkit 1/1] notifications: matchers: add nested matcher support
Lukas Wagner
l.wagner at proxmox.com
Wed May 21 16:23:08 CEST 2025
Adds a new checkbox to declare a matcher is 'nested', as well as a
new node type in the rule tree 'Evaluate nested matcher'. The latter
allows one to select other matcher which is declared as 'nested'.
Signed-off-by: Lukas Wagner <l.wagner at proxmox.com>
---
src/data/model/NotificationConfig.js | 8 +-
src/panel/NotificationConfigView.js | 6 +
src/window/NotificationMatcherEdit.js | 220 +++++++++++++++++++++++++-
3 files changed, 232 insertions(+), 2 deletions(-)
diff --git a/src/data/model/NotificationConfig.js b/src/data/model/NotificationConfig.js
index 03cf317..e1f7058 100644
--- a/src/data/model/NotificationConfig.js
+++ b/src/data/model/NotificationConfig.js
@@ -9,7 +9,13 @@ Ext.define('proxmox-notification-endpoints', {
Ext.define('proxmox-notification-matchers', {
extend: 'Ext.data.Model',
- fields: ['name', 'comment', 'disable', 'origin'],
+ fields: [
+ 'name',
+ 'comment',
+ 'disable',
+ 'origin',
+ 'nested',
+ ],
proxy: {
type: 'proxmox',
},
diff --git a/src/panel/NotificationConfigView.js b/src/panel/NotificationConfigView.js
index 9505eb7..994ff9d 100644
--- a/src/panel/NotificationConfigView.js
+++ b/src/panel/NotificationConfigView.js
@@ -326,6 +326,12 @@ Ext.define('Proxmox.panel.NotificationMatcherView', {
renderer: (disable) => Proxmox.Utils.renderEnabledIcon(!disable),
align: 'center',
},
+ {
+ dataIndex: 'nested',
+ text: gettext('Nested'),
+ renderer: (nested) => Proxmox.Utils.renderEnabledIcon(nested),
+ align: 'center',
+ },
{
dataIndex: 'name',
text: gettext('Matcher Name'),
diff --git a/src/window/NotificationMatcherEdit.js b/src/window/NotificationMatcherEdit.js
index 83c09ea..06597d5 100644
--- a/src/window/NotificationMatcherEdit.js
+++ b/src/window/NotificationMatcherEdit.js
@@ -21,6 +21,21 @@ Ext.define('Proxmox.panel.NotificationMatcherGeneralPanel', {
allowBlank: false,
checked: true,
},
+ {
+ xtype: 'proxmoxcheckbox',
+ name: 'nested',
+ fieldLabel: gettext('Nested matcher'),
+ allowBlank: false,
+ checked: false,
+ bind: '{isNestedMatcher}',
+ autoEl: {
+ tag: 'div',
+ 'data-qtip': gettext('Nested matchers can be used by other matchers to create sophisticated matching rules.'),
+ },
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ },
{
xtype: 'proxmoxtextfield',
name: 'comment',
@@ -64,9 +79,24 @@ Ext.define('Proxmox.panel.NotificationMatcherTargetPanel', {
{
xtype: 'pmxNotificationTargetSelector',
name: 'target',
- allowBlank: false,
+ allowBlank: true,
},
],
+
+ onGetValues: function(values) {
+ let me = this;
+
+ if (Ext.isArray(values.target)) {
+ if (values.target.length === 0) {
+ delete values.target;
+ if (!me.isCreate) {
+ Proxmox.Utils.assemble_field_data(values, { 'delete': 'target' });
+ }
+ }
+ }
+
+ return values;
+ },
});
Ext.define('Proxmox.window.NotificationMatcherEdit', {
@@ -81,6 +111,12 @@ Ext.define('Proxmox.window.NotificationMatcherEdit', {
width: 800,
+ viewModel: {
+ data: {
+ isNestedMatcher: false,
+ },
+ },
+
initComponent: function() {
let me = this;
@@ -130,6 +166,9 @@ Ext.define('Proxmox.window.NotificationMatcherEdit', {
xtype: 'pmxNotificationMatcherTargetPanel',
isCreate: me.isCreate,
baseUrl: me.baseUrl,
+ bind: {
+ disabled: '{isNestedMatcher}',
+ },
},
],
},
@@ -357,6 +396,11 @@ Ext.define('Proxmox.panel.NotificationRulesEditPanel', {
value: '',
};
break;
+ case 'eval-matcher':
+ data = {
+ matcher: '',
+ };
+ break;
}
let node = {
@@ -424,6 +468,7 @@ Ext.define('Proxmox.panel.NotificationRulesEditPanel', {
xtype: 'pmxNotificationMatchRuleSettings',
cbind: {
baseUrl: '{baseUrl}',
+ name: '{name}',
},
},
@@ -445,6 +490,7 @@ Ext.define('Proxmox.panel.NotificationRulesEditPanel', {
deleteArrayIfEmtpy('match-field');
deleteArrayIfEmtpy('match-severity');
deleteArrayIfEmtpy('match-calendar');
+ deleteArrayIfEmtpy('eval-matcher');
return values;
},
@@ -506,6 +552,14 @@ Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
iconCls = 'fa fa-filter';
break;
+ case 'eval-matcher': {
+ let v = data.matcher;
+ text = Ext.String.format(gettext("Evaluate nested matcher: {0}"), v);
+ iconCls = 'fa fa-filter';
+ if (!v) {
+ iconCls += ' internal-error';
+ }
+ } break;
}
return [text, iconCls];
@@ -632,12 +686,29 @@ Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
deleteEmpty: !me.isCreate,
});
+ let realEvalMatcher = Ext.create({
+ xtype: 'hiddenfield',
+ name: 'eval-matcher',
+ setValue: function(value) {
+ this.value = value;
+ this.checkChange();
+ },
+ getValue: function() {
+ return this.value;
+ },
+ getSubmitValue: function() {
+ let value = this.value;
+ return value;
+ },
+ });
+
let storeChanged = function(store) {
store.suspendEvent('datachanged');
let matchFieldStmts = [];
let matchSeverityStmts = [];
let matchCalendarStmts = [];
+ let evalMatcherStmts = [];
let modeStmt = 'all';
let invertMatchStmt = false;
@@ -663,6 +734,9 @@ Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
modeStmt = data.value;
invertMatchStmt = data.invert;
break;
+ case 'eval-matcher':
+ evalMatcherStmts.push(data.matcher);
+ break;
}
let [text, iconCls] = me.getNodeTextAndIcon(type, data);
@@ -692,6 +766,10 @@ Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
realMatchSeverity.setValue(matchSeverityStmts);
realMatchSeverity.resumeEvent('change');
+ realEvalMatcher.suspendEvent('change');
+ realEvalMatcher.setValue(evalMatcherStmts);
+ realEvalMatcher.resumeEvent('change');
+
store.resumeEvent('datachanged');
};
@@ -800,6 +878,30 @@ Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
});
});
+ realEvalMatcher.addListener('change', function(field, value) {
+ let parseEvalMatcher = function(matcher) {
+ return {
+ type: 'eval-matcher',
+ data: {
+ matcher: matcher,
+ },
+ leaf: true,
+ };
+ };
+
+ for (let node of treeStore.queryBy(
+ record => record.get('type') === 'eval-matcher').getRange()) {
+ node.remove(true);
+ }
+
+ let records = value.map(parseEvalMatcher);
+ let rootNode = treeStore.getRootNode();
+
+ for (let record of records) {
+ rootNode.appendChild(record);
+ }
+ });
+
treeStore.addListener('datachanged', storeChanged);
let treePanel = Ext.create({
@@ -844,6 +946,7 @@ Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
realMatchSeverity,
realInvertMatch,
realMatchCalendar,
+ realEvalMatcher,
treePanel,
{
xtype: 'button',
@@ -913,6 +1016,7 @@ Ext.define('Proxmox.panel.NotificationMatchRuleSettings', {
['match-field', gettext('Match Field')],
['match-severity', gettext('Match Severity')],
['match-calendar', gettext('Match Calendar')],
+ ['eval-matcher', gettext('Evaluate nested matcher')],
],
},
{
@@ -927,6 +1031,13 @@ Ext.define('Proxmox.panel.NotificationMatchRuleSettings', {
{
xtype: 'pmxNotificationMatchCalendarSettings',
},
+ {
+ xtype: 'pmxNotificationEvalMatcherSettings',
+ cbind: {
+ baseUrl: '{baseUrl}',
+ name: '{name}',
+ },
+ },
],
});
@@ -1372,3 +1483,110 @@ Ext.define('Proxmox.panel.MatchFieldSettings', {
me.callParent();
},
});
+
+Ext.define('Proxmox.panel.EvalMatcherSettings', {
+ extend: 'Ext.panel.Panel',
+ xtype: 'pmxNotificationEvalMatcherSettings',
+ border: false,
+ layout: 'anchor',
+ // Hide initially to avoid glitches when opening the window
+ hidden: true,
+ bind: {
+ hidden: '{!typeIsEvalMatcher}',
+ },
+ viewModel: {
+ // parent is set in `initComponents`
+ formulas: {
+ typeIsEvalMatcher: {
+ bind: {
+ bindTo: '{selectedRecord}',
+ deep: true,
+ },
+ get: function(record) {
+ return record?.get('type') === 'eval-matcher';
+ },
+ },
+ evalMatcherValue: {
+ bind: {
+ bindTo: '{selectedRecord}',
+ deep: true,
+ },
+ set: function(value) {
+ let record = this.get('selectedRecord');
+ let currentData = record.get('data');
+ record.set({
+ data: {
+ ...currentData,
+ matcher: value,
+ },
+ });
+ },
+ get: function(record) {
+ return record?.get('data')?.matcher;
+ },
+ },
+ },
+ },
+
+ initComponent: function() {
+ let me = this;
+
+ let valueStore = Ext.create('Ext.data.Store', {
+ model: 'proxmox-notification-matchers',
+ autoLoad: true,
+ proxy: {
+ type: 'proxmox',
+ url: `/api2/json/${me.baseUrl}/matchers`,
+ },
+ filters: [
+ {
+ property: 'nested',
+ value: true,
+ },
+ (item) => item.get('name') !== me.name,
+ ],
+ });
+
+ Ext.apply(me.viewModel, {
+ parent: me.up('pmxNotificationMatchRulesEditPanel').getViewModel(),
+ });
+ Ext.apply(me, {
+ items: [
+ {
+ fieldLabel: gettext('Matcher'),
+ xtype: 'proxmoxComboGrid',
+ autoSelect: false,
+ editable: false,
+ isFormField: false,
+ submitValue: false,
+ allowBlank: false,
+ showClearTrigger: true,
+ field: 'name',
+ store: valueStore,
+ valueField: 'name',
+ displayField: 'name',
+ notFoundIsValid: false,
+ multiSelect: false,
+ bind: {
+ value: '{evalMatcherValue}',
+ },
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Name'),
+ dataIndex: 'name',
+ flex: 1,
+ },
+ {
+ header: gettext('Comment'),
+ dataIndex: 'comment',
+ flex: 2,
+ },
+ ],
+ },
+ },
+ ],
+ });
+ me.callParent();
+ },
+});
--
2.39.5
More information about the pbs-devel
mailing list