From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pbs-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id ED7451FF183 for <inbox@lore.proxmox.com>; Wed, 21 May 2025 16:23:50 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 83AB51A5FE; Wed, 21 May 2025 16:23:49 +0200 (CEST) From: Lukas Wagner <l.wagner@proxmox.com> To: pbs-devel@lists.proxmox.com Date: Wed, 21 May 2025 16:23:08 +0200 Message-Id: <20250521142309.264719-4-l.wagner@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250521142309.264719-1-l.wagner@proxmox.com> References: <20250521142309.264719-1-l.wagner@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.032 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment PROLO_LEO1 0.1 Meta Catches all Leo drug variations so far RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [PATCH proxmox-widget-toolkit 1/1] notifications: matchers: add nested matcher support X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion <pbs-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/> List-Post: <mailto:pbs-devel@lists.proxmox.com> List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox Backup Server development discussion <pbs-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" <pbs-devel-bounces@lists.proxmox.com> 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@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 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel