From 09872eec8e16aee8de09ba5bc256b7376c264ff1 Mon Sep 17 00:00:00 2001 From: Lukas <76838159+wolflu05@users.noreply.github.com> Date: Mon, 11 Sep 2023 02:57:42 +0200 Subject: [PATCH] [0.12.x] Fix missing filters for get settings validator (#5480) (#5522) * Fix missing filters for get settings validator (#5480) * Fix missing filters for get settings validator * merge default model instance filters and kwargs * Added tests for validators * Give it a try without the kwargs passed to clean in save function * Added string for identification for debug statement * Added more debug comments * Added more debug prints * Fix test debug * Modiefied workflow * trigger ci * Fix test and remove unused kwargs * Added debug prints * Only run one test in ci * Added more debug code * Remove all debug prints and reset workflow * Reset overlooked file (cherry picked from commit 9a6c2d2953adebda8a7ca0262cd3e1f3bb2e7c6a) # Conflicts: # InvenTree/plugin/samples/integration/test_sample.py * Add missing import * Added second missing import --- InvenTree/common/models.py | 6 ++-- InvenTree/common/tests.py | 32 +++++++++++++++++++ .../plugin/samples/integration/sample.py | 16 ++++++++++ .../plugin/samples/integration/test_sample.py | 17 ++++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 0fa1f14660..0e15fd124c 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -166,7 +166,7 @@ class BaseInvenTreeSetting(models.Model): do_cache = kwargs.pop('cache', True) - self.clean(**kwargs) + self.clean() self.validate_unique() # Execute before_save action @@ -560,7 +560,7 @@ class BaseInvenTreeSetting(models.Model): """Return units for setting.""" return self.__class__.get_setting_units(self.key, **self.get_filters_for_instance()) - def clean(self, **kwargs): + def clean(self): """If a validator (or multiple validators) are defined for a particular setting key, run them against the 'value' field.""" super().clean() @@ -571,7 +571,7 @@ class BaseInvenTreeSetting(models.Model): elif self.is_bool(): self.value = self.as_bool() - validator = self.__class__.get_setting_validator(self.key, **kwargs) + validator = self.__class__.get_setting_validator(self.key, **self.get_filters_for_instance()) if validator is not None: self.run_validator(validator) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 4402a16933..9d0bb447b5 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -5,9 +5,11 @@ import json import time from datetime import timedelta from http import HTTPStatus +from unittest import mock from django.contrib.auth import get_user_model from django.core.cache import cache +from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile from django.test import Client, TestCase from django.urls import reverse @@ -106,6 +108,36 @@ class SettingsTest(InvenTreeTestCase): self.assertIn('STOCK_OWNERSHIP_CONTROL', result) self.assertIn('SIGNUP_GROUP', result) + @mock.patch("common.models.InvenTreeSetting.get_setting_definition") + def test_settings_validator(self, get_setting_definition): + """Make sure that the validator function gets called on set setting.""" + + def validator(x): + if x == "hello": + return x + + raise ValidationError(f"{x} is not valid") + + mock_validator = mock.Mock(side_effect=validator) + + # define partial schema + settings_definition = { + "AB": { # key that's has not already been accessed + "validator": mock_validator, + }, + } + + def mocked(key, **kwargs): + return settings_definition.get(key, {}) + get_setting_definition.side_effect = mocked + + InvenTreeSetting.set_setting("AB", "hello", self.user) + mock_validator.assert_called_with("hello") + + with self.assertRaises(ValidationError): + InvenTreeSetting.set_setting("AB", "world", self.user) + mock_validator.assert_called_with("world") + def run_settings_check(self, key, setting): """Test that all settings are valid. diff --git a/InvenTree/plugin/samples/integration/sample.py b/InvenTree/plugin/samples/integration/sample.py index 7a04fbbcca..1a40498c85 100644 --- a/InvenTree/plugin/samples/integration/sample.py +++ b/InvenTree/plugin/samples/integration/sample.py @@ -1,5 +1,8 @@ """Sample implementations for IntegrationPlugin.""" +import json + +from django.core.exceptions import ValidationError from django.http import HttpResponse from django.urls import include, re_path from django.utils.translation import gettext_lazy as _ @@ -8,6 +11,14 @@ from plugin import InvenTreePlugin from plugin.mixins import AppMixin, NavigationMixin, SettingsMixin, UrlsMixin +def validate_json(value): + """Example validator for json input.""" + try: + json.loads(value) + except Exception as e: + raise ValidationError(str(e)) + + class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, InvenTreePlugin): """A full plugin example.""" @@ -77,6 +88,11 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi 'description': 'A protected setting, hidden from the UI', 'default': 'ABC-123', 'protected': True, + }, + 'VALIDATOR_SETTING': { + 'name': 'JSON validator Setting', + 'description': 'A setting using a JSON validator', + 'validator': validate_json, } } diff --git a/InvenTree/plugin/samples/integration/test_sample.py b/InvenTree/plugin/samples/integration/test_sample.py index 1aad91928e..0a97ea8b0a 100644 --- a/InvenTree/plugin/samples/integration/test_sample.py +++ b/InvenTree/plugin/samples/integration/test_sample.py @@ -1,6 +1,9 @@ """Unit tests for action plugins.""" +from django.core.exceptions import ValidationError + from InvenTree.unit_test import InvenTreeTestCase +from plugin import registry class SampleIntegrationPluginTests(InvenTreeTestCase): @@ -11,3 +14,17 @@ class SampleIntegrationPluginTests(InvenTreeTestCase): response = self.client.get('/plugin/sample/ho/he/') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'Hi there testuser this works') + + def test_settings_validator(self): + """Test settings validator for plugins.""" + + plugin = registry.get_plugin('sample') + valid_json = '{"ts": 13}' + not_valid_json = '{"ts""13"}' + + # no error, should pass validator + plugin.set_setting('VALIDATOR_SETTING', valid_json) + + # should throw an error + with self.assertRaises(ValidationError): + plugin.set_setting('VALIDATOR_SETTING', not_valid_json)