diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fecdb4656d..d3eb94d364 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog ========= +Upgrade notes +------------- + +* [:backend:`4810`] Fixed uppercase component variable values turing lowercase. + There are manual actions required. + 2.8.1 (2024-10-29) ================== diff --git a/bin/fix_selectboxes_component_default_values.py b/bin/fix_selectboxes_component_default_values.py new file mode 100644 index 0000000000..5b6aea6440 --- /dev/null +++ b/bin/fix_selectboxes_component_default_values.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Fix the initial values of form variables related to selectboxes components. +# +# Due to a bug in the JSON parser, some of the form variable initial value don't match with +# their respective component default values. This bug is mostly an issue for selectboxes components. +# The problem in the JSON parser has been fixed, so new and updated forms won't have this issue. +# +# This script automatically fixes all initial values of the selectboxes component form variables. +# Related to ticket: https://github.com/open-formulieren/open-forms/issues/4810 +from __future__ import annotations + +import sys +from pathlib import Path + +import django + +SRC_DIR = Path(__file__).parent.parent / "src" +sys.path.insert(0, str(SRC_DIR.resolve())) + + +def fix_selectboxes_default_values(): + from django.db.models import Q + + from openforms.formio.utils import get_component_default_value + from openforms.forms.models import FormDefinition, FormVariable + + variables_to_update = [] + fds = FormDefinition.objects.iterator() + for form_definition in fds: + for component in form_definition.iter_components(): + # The fix is only needed for selectboxes components + if component["type"] != "selectboxes": + continue + + # Update all form variables related to the component and form definition, + # when the form variable initial value doesn't match the component default value + form_variables = FormVariable.objects.filter( + ~Q(initial_value=get_component_default_value(component)), + key=component["key"], + form_definition_id=form_definition.id, + ) + for form_variable in form_variables: + form_variable.initial_value = get_component_default_value(component) + variables_to_update.append(form_variable) + + FormVariable.objects.bulk_update(variables_to_update, fields=["initial_value"]) + + +def main(**kwargs): + from openforms.setup import setup_env + + setup_env() + django.setup() + + return fix_selectboxes_default_values(**kwargs) + + +if __name__ == "__main__": + main() diff --git a/src/openforms/forms/api/parsers.py b/src/openforms/forms/api/parsers.py index 92dd6a6907..b414469a3c 100644 --- a/src/openforms/forms/api/parsers.py +++ b/src/openforms/forms/api/parsers.py @@ -17,10 +17,13 @@ class IgnoreConfigurationFieldCamelCaseJSONParser(CamelCaseJSONParser): class FormVariableJSONParser(CamelCaseJSONParser): + # The field initial_value needs to be ignored, to prevent accidental uppercase to lowercase changes. + # See github issue https://github.com/open-formulieren/open-forms/issues/4810 json_underscoreize = { "ignore_fields": ( "body", "headers", + "initial_value", "mapping_expression", "query_params", ) @@ -28,10 +31,13 @@ class FormVariableJSONParser(CamelCaseJSONParser): class FormVariableJSONRenderer(CamelCaseJSONRenderer): + # The field initial_value needs to be ignored, to prevent accidental uppercase to lowercase changes. + # See github issue https://github.com/open-formulieren/open-forms/issues/4810 json_underscoreize = { "ignore_fields": ( "body", "headers", + "initial_value", "mapping_expression", "query_params", ) diff --git a/src/openforms/forms/tests/variables/test_viewset.py b/src/openforms/forms/tests/variables/test_viewset.py index 63eec99534..27e4dba7a5 100644 --- a/src/openforms/forms/tests/variables/test_viewset.py +++ b/src/openforms/forms/tests/variables/test_viewset.py @@ -936,3 +936,56 @@ def test_validate_prefill_consistency(self): error["invalidParams"][0]["name"], "0.prefillAttribute", ) + + def test_bulk_create_and_update_with_non_camel_case_initial_values(self): + user = StaffUserFactory.create(user_permissions=["change_form"]) + self.client.force_authenticate(user) + + form = FormFactory.create(generate_minimal_setup=True) + form_definition = form.formstep_set.get().form_definition + form_path = reverse("api:form-detail", kwargs={"uuid_or_slug": form.uuid}) + form_url = f"http://testserver.com{form_path}" + form_definition_path = reverse( + "api:formdefinition-detail", kwargs={"uuid": form_definition.uuid} + ) + form_definition_url = f"http://testserver.com{form_definition_path}" + + data = [ + { + "form": form_url, + "form_definition": form_definition_url, + "key": form_definition.configuration["components"][0]["key"], + "name": "Test", + "service_fetch_configuration": None, + "data_type": FormVariableDataTypes.object, + "source": FormVariableSources.component, + "initial_value": { + "VALUE IN UPPERCASE": True, + "VALUE-IN-UPPER-KEBAB-CASE": True, + "VALUE_IN_UPPER_SNAKE_CASE": False, + "value in lower case": False, + "value-in-lower-kebab-case": False, + "value_in_lower_snake_case": True, + }, + } + ] + + response = self.client.put( + reverse( + "api:form-variables", + kwargs={"uuid_or_slug": form.uuid}, + ), + data=data, + ) + + expected_initial_value = { + "VALUE IN UPPERCASE": True, + "VALUE-IN-UPPER-KEBAB-CASE": True, + "VALUE_IN_UPPER_SNAKE_CASE": False, + "value in lower case": False, + "value-in-lower-kebab-case": False, + "value_in_lower_snake_case": True, + } + + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(expected_initial_value, response.json()[0]["initialValue"])