2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-09-13 22:21:37 +00:00

[Refactor] Notification plugins (#9735)

* Refactor notification concept

- Notifications handled by plugins

* Cleanup

* Only send email if template provided in context

* Logic cleanup

* Fix log_error call

* Refactor error logging

- Ensure plugin slug is correctly attached
- Consistent format
- Logic fixes

* More robust plugin lookup

* Refactor calls to tringger_notification

* Tweak for build stock notification

* Low stock notification refactor

- Actually *use* the notification system
- Fix for email template

* Check stock only when build is issued

* Updated documentation

* Add PluginUserSetting class

- Allows plugins to define per-user settings

* Add API endpoints for PluginUserSetting model

* Placeholder for user-plugin-settings page

* Refactoring frontend code

* Placeholder panel

* Adds user interface for changing user-specific plugin settings

* Tweaks

* Remove old model

* Update documentation

* Playwright tests

* Update API version

* Fix unit test

* Fix removed arg

* Fixes for email notifications

- Track status of sending notifications
- Add helper "activate" method for plugin class
- Update unit tests

* Fix barcode tests

* More unit test fixes

* Test fixes

* Fix for settings models with extra fields

* Enhance unit test

* Remove old test file

* Check for null target_fnc

* Improve DB query efficiency

- Provide a flat list of active keys to plugin.is_active
- Prevents DB fetching (in certain circumstances)
- Add registry.active_plugins() method

* Bump query limit up for test

- In practice, this API endpoint is ~10 queries

* Handle potential errors

* Increase query limit for API test

* Increase query limit for some tests

* Bump API version

* Tweak unit test

* Tweak unit test

* Increased allowed queries

* fix user plugin settings

* Fix for unit test

* Update debug msg

* Tweak API

* Fix endpoint

* Remove "active plugin keys" code

* Restore previous behaviour

* Fix unit tests

* Tweak unit test

* Update src/backend/InvenTree/build/tasks.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/backend/InvenTree/plugin/base/integration/NotificationMixin.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Func updates

* Format

* Add notification settings

* Refactor plugin settings groups

* Fix func type

* Adjust message

* Additional unit tests

* Additional playwright tests

* Additional playwright test

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Oliver
2025-07-26 13:05:59 +10:00
committed by GitHub
parent f862867e2b
commit 1085625af4
60 changed files with 1329 additions and 972 deletions

View File

@@ -65,6 +65,47 @@ test('Plugins - Settings', async ({ browser, request }) => {
await page.getByText('Mouser Electronics').click();
});
test('Plugins - User Settings', async ({ browser, request }) => {
const page = await doCachedLogin(browser);
// Ensure that the SampleIntegration plugin is enabled
await setPluginState({
request,
plugin: 'sample',
state: true
});
// Navigate to user settings
await navigate(page, 'settings/user/');
await loadTab(page, 'Plugin Settings');
// User settings for the "Sample Plugin" should be visible
await page.getByRole('button', { name: 'Sample Plugin' }).click();
await page.getByText('User Setting 1').waitFor();
await page.getByText('User Setting 2').waitFor();
await page.getByText('User Setting 3').waitFor();
// Check for expected setting options
await page.getByLabel('edit-setting-USER_SETTING_3').click();
const val = await page.getByLabel('choice-field-value').inputValue();
await page.getByLabel('choice-field-value').click();
await page.getByRole('option', { name: 'Choice X' }).waitFor();
await page.getByRole('option', { name: 'Choice Y' }).waitFor();
await page.getByRole('option', { name: 'Choice Z' }).waitFor();
// Change the value of USER_SETTING_3
await page
.getByRole('option', { name: val == 'Choice X' ? 'Choice Z' : 'Choice X' })
.click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Setting USER_SETTING_3 updated successfully').waitFor();
});
// Test base plugin functionality
test('Plugins - Functionality', async ({ browser }) => {
// Navigate and select the plugin

View File

@@ -2,7 +2,7 @@ import { expect, test } from './baseFixtures.js';
import { apiUrl } from './defaults.js';
import { getRowFromCell, loadTab, navigate } from './helpers.js';
import { doCachedLogin } from './login.js';
import { setSettingState } from './settings.js';
import { setPluginState, setSettingState } from './settings.js';
/**
* Adjust language and color settings
@@ -80,6 +80,97 @@ test('Settings - User theme', async ({ browser }) => {
await page.getByLabel('#228be6').click();
});
test('Settings - User', async ({ browser }) => {
const page = await doCachedLogin(browser, {
username: 'allaccess',
password: 'nolimits',
url: 'settings/user/'
});
await loadTab(page, 'Account');
await page.getByText('Account Details').waitFor();
await page.getByText('Profile Details').waitFor();
await loadTab(page, 'Security');
await page.getByRole('button', { name: 'Single Sign On' }).waitFor();
await page.getByRole('button', { name: 'Access Tokens' }).waitFor();
await loadTab(page, 'Display Options');
await page
.getByText('The navbar position is fixed to the top of the screen')
.waitFor();
await page.getByText('Escape Key Closes Forms').waitFor();
await loadTab(page, 'Search');
await page.getByText('Whole Word Search').waitFor();
await page.getByText('Hide Unavailable Stock Items').waitFor();
await loadTab(page, 'Notifications');
await page
.getByRole('button', { name: 'InvenTree Email Notifications' })
.waitFor();
await loadTab(page, 'Reporting');
await page.getByText('Inline report display').waitFor();
await loadTab(page, 'Plugin Settings');
await page
.getByRole('button', { name: 'InvenTree Email Notifications' })
.waitFor();
});
test('Settings - Global', async ({ browser, request }) => {
const page = await doCachedLogin(browser, {
username: 'steven',
password: 'wizardstaff',
url: 'settings/system/'
});
// Ensure the "slack" notification plugin is enabled
// This is to ensure it is visible in the "notification" settings tab
await setPluginState({
request,
plugin: 'inventree-slack-notification',
state: true
});
await loadTab(page, 'Server');
await loadTab(page, 'Authentication');
await loadTab(page, 'Barcodes');
await loadTab(page, 'Pricing');
await loadTab(page, 'Parts');
await loadTab(page, 'Stock');
await loadTab(page, 'Notifications');
await page
.getByText(
'The settings below are specific to each available notification method'
)
.waitFor();
await page
.getByRole('button', { name: 'InvenTree Slack Notifications' })
.click();
await page.getByText('Slack incoming webhook url').waitFor();
await page
.getByText('URL that is used to send messages to a slack channel')
.waitFor();
await loadTab(page, 'Plugin Settings');
await page
.getByText('The settings below are specific to each available plugin')
.waitFor();
await page
.getByRole('button', { name: 'InvenTree Barcodes Provides' })
.waitFor();
await page
.getByRole('button', { name: 'InvenTree PDF label printer' })
.waitFor();
await page
.getByRole('button', { name: 'InvenTree Slack Notifications' })
.waitFor();
});
test('Settings - Admin', async ({ browser }) => {
// Note here we login with admin access
const page = await doCachedLogin(browser, {