mirror of
https://github.com/netbox-community/netbox.git
synced 2026-02-03 22:06:26 -06:00
Fixes #20977: Apply defaults for missing script variables (#21295)
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CI / build (20.x, 3.14) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CI / build (20.x, 3.14) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Ensure script variables fall back to their defined defaults when a value is not submitted (e.g. via "Run again" or other minimal POSTs). - Populate omitted script variables with their initial/default values before validation and job enqueueing - Treat falsy defaults (e.g. False/0) as valid defaults - Add a test asserting defaults are included in enqueued job data - Remove the redundant default from ScriptValidationErrorTest
This commit is contained in:
@@ -61,7 +61,7 @@ class ScriptVariable:
|
||||
self.field_attrs['label'] = label
|
||||
if description:
|
||||
self.field_attrs['help_text'] = description
|
||||
if default:
|
||||
if default is not None:
|
||||
self.field_attrs['initial'] = default
|
||||
if widget:
|
||||
self.field_attrs['widget'] = widget
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.urls import reverse
|
||||
from django.test import tag
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
from core.choices import ManagedFileRootPathChoices
|
||||
from core.events import *
|
||||
@@ -906,7 +907,7 @@ class ScriptValidationErrorTest(TestCase):
|
||||
user_permissions = ['extras.view_script', 'extras.run_script']
|
||||
|
||||
class TestScriptMixin:
|
||||
bar = IntegerVar(min_value=0, max_value=30, default=30)
|
||||
bar = IntegerVar(min_value=0, max_value=30)
|
||||
|
||||
class TestScriptClass(TestScriptMixin, PythonClass):
|
||||
class Meta:
|
||||
@@ -930,8 +931,6 @@ class ScriptValidationErrorTest(TestCase):
|
||||
|
||||
@tag('regression')
|
||||
def test_script_validation_error_displays_message(self):
|
||||
from unittest.mock import patch
|
||||
|
||||
url = reverse('extras:script', kwargs={'pk': self.script.pk})
|
||||
|
||||
with patch('extras.views.get_workers_for_queue', return_value=['worker']):
|
||||
@@ -944,8 +943,6 @@ class ScriptValidationErrorTest(TestCase):
|
||||
|
||||
@tag('regression')
|
||||
def test_script_validation_error_no_toast_for_fieldset_fields(self):
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
class FieldsetScript(PythonClass):
|
||||
class Meta:
|
||||
name = 'Fieldset test'
|
||||
@@ -967,3 +964,42 @@ class ScriptValidationErrorTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
messages = list(response.context['messages'])
|
||||
self.assertEqual(len(messages), 0)
|
||||
|
||||
|
||||
class ScriptDefaultValuesTest(TestCase):
|
||||
user_permissions = ['extras.view_script', 'extras.run_script']
|
||||
|
||||
class TestScriptClass(PythonClass):
|
||||
class Meta:
|
||||
name = 'Test script'
|
||||
commit_default = False
|
||||
|
||||
bool_default_true = BooleanVar(default=True)
|
||||
bool_default_false = BooleanVar(default=False)
|
||||
int_with_default = IntegerVar(default=0)
|
||||
int_without_default = IntegerVar(required=False)
|
||||
|
||||
def run(self, data, commit):
|
||||
return "Complete"
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
module = ScriptModule.objects.create(file_root=ManagedFileRootPathChoices.SCRIPTS, file_path='test_script.py')
|
||||
cls.script = Script.objects.create(module=module, name='Test script', is_executable=True)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
Script.python_class = property(lambda self: ScriptDefaultValuesTest.TestScriptClass)
|
||||
|
||||
def test_default_values_are_used(self):
|
||||
url = reverse('extras:script', kwargs={'pk': self.script.pk})
|
||||
|
||||
with patch('extras.views.get_workers_for_queue', return_value=['worker']):
|
||||
with patch('extras.jobs.ScriptJob.enqueue') as mock_enqueue:
|
||||
mock_enqueue.return_value.pk = 1
|
||||
self.client.post(url, {})
|
||||
call_kwargs = mock_enqueue.call_args.kwargs
|
||||
self.assertEqual(call_kwargs['data']['bool_default_true'], True)
|
||||
self.assertEqual(call_kwargs['data']['bool_default_false'], False)
|
||||
self.assertEqual(call_kwargs['data']['int_with_default'], 0)
|
||||
self.assertIsNone(call_kwargs['data']['int_without_default'])
|
||||
|
||||
@@ -1511,7 +1511,13 @@ class ScriptView(BaseScriptView):
|
||||
'script': script,
|
||||
})
|
||||
|
||||
form = script_class.as_form(request.POST, request.FILES)
|
||||
# Populate missing variables with their default values, if defined
|
||||
post_data = request.POST.copy()
|
||||
for name, var in script_class._get_vars().items():
|
||||
if name not in post_data and (initial := var.field_attrs.get('initial')) is not None:
|
||||
post_data[name] = initial
|
||||
|
||||
form = script_class.as_form(post_data, request.FILES)
|
||||
|
||||
# Allow execution only if RQ worker process is running
|
||||
if not get_workers_for_queue('default'):
|
||||
|
||||
Reference in New Issue
Block a user