diff --git a/src/servala/api/views.py b/src/servala/api/views.py index 015e091..5fdb91a 100644 --- a/src/servala/api/views.py +++ b/src/servala/api/views.py @@ -239,9 +239,9 @@ The Servala Team""" service_offering = ServiceOffering.objects.get( osb_plan_id=plan_id, service=service ) - except Service.DoesNotExist: # pragma: no-cover + except Service.DoesNotExist: return self._error(f"Unknown service_id: {service_id}") - except ServiceOffering.DoesNotExist: # pragma: no-cover + except ServiceOffering.DoesNotExist: return self._error( f"Unknown plan_id: {plan_id} for service_id: {service_id}" ) @@ -284,7 +284,7 @@ The Servala Team""" if service_instance: organization = service_instance.organization - except Exception: # pragma: no cover + except Exception: pass description_parts = [f"Action: {action}", f"Service: {service.name}"] diff --git a/src/servala/core/crd/forms.py b/src/servala/core/crd/forms.py index 0f825ce..659684e 100644 --- a/src/servala/core/crd/forms.py +++ b/src/servala/core/crd/forms.py @@ -1,12 +1,10 @@ -from contextlib import suppress - from django import forms from django.core.validators import MaxValueValidator, MinValueValidator from django.forms.models import ModelForm, ModelFormMetaclass from servala.core.crd.utils import deslugify from servala.core.models import ControlPlaneCRD -from servala.frontend.forms.widgets import DynamicArrayWidget, NumberInputWithAddon +from servala.frontend.forms.widgets import DynamicArrayWidget # Fields that must be present in every form MANDATORY_FIELDS = ["name"] @@ -27,11 +25,6 @@ DEFAULT_FIELD_CONFIGS = { "help_text": "Domain names for accessing this service", "required": False, }, - "spec.parameters.size.disk": { - "type": "number", - "label": "Disk size", - "addon_text": "Gi", - }, } @@ -342,19 +335,6 @@ class CustomFormMixin(FormGeneratorMixin): if field_type == "number": min_val = field_config.get("min_value") max_val = field_config.get("max_value") - unit = field_config.get("addon_text") - - if unit: - field.widget = NumberInputWithAddon(addon_text=unit) - field.addon_text = unit - value = self.initial.get(field_name) - if value and isinstance(value, str) and value.endswith(unit): - numeric_value = value[: -len(unit)] - with suppress(ValueError): - if "." in numeric_value: - self.initial[field_name] = float(numeric_value) - else: - self.initial[field_name] = int(numeric_value) validators = [] if min_val is not None: @@ -426,11 +406,6 @@ class CustomFormMixin(FormGeneratorMixin): mapping = field_name value = self.cleaned_data.get(field_name) - field = self.fields[field_name] - - if addon_text := getattr(field, "addon_text", None): - value = f"{value}{addon_text}" - parts = mapping.split(".") current = nested for part in parts[:-1]: diff --git a/src/servala/frontend/forms/widgets.py b/src/servala/frontend/forms/widgets.py index 99b7a59..d67030f 100644 --- a/src/servala/frontend/forms/widgets.py +++ b/src/servala/frontend/forms/widgets.py @@ -2,7 +2,6 @@ import json from django import forms from django.core.exceptions import ValidationError -from django.forms.widgets import NumberInput class DynamicArrayWidget(forms.Widget): @@ -217,21 +216,3 @@ class DynamicArrayField(forms.JSONField): raise ValidationError( f"Item {i + 1} must be one of: {', '.join(enum_values)}" ) - - -class NumberInputWithAddon(NumberInput): - """ - Widget for number input fields with a suffix add-on (e.g., "Gi", "MB"). - Renders as a Bootstrap input-group with the suffix displayed as an add-on. - """ - - template_name = "frontend/forms/number_input_with_addon.html" - - def __init__(self, addon_text="", attrs=None): - super().__init__(attrs) - self.addon_text = addon_text - - def get_context(self, name, value, attrs): - context = super().get_context(name, value, attrs) - context["widget"]["addon_text"] = self.addon_text - return context diff --git a/src/servala/frontend/templates/frontend/forms/number_input_with_addon.html b/src/servala/frontend/templates/frontend/forms/number_input_with_addon.html deleted file mode 100644 index 4fe3b54..0000000 --- a/src/servala/frontend/templates/frontend/forms/number_input_with_addon.html +++ /dev/null @@ -1,11 +0,0 @@ -
- - {{ widget.addon_text }} -
diff --git a/src/tests/test_form_config.py b/src/tests/test_form_config.py index c93f3fb..7188d61 100644 --- a/src/tests/test_form_config.py +++ b/src/tests/test_form_config.py @@ -10,6 +10,28 @@ from servala.core.forms import ServiceDefinitionAdminForm from servala.core.models import ControlPlaneCRD +def test_custom_model_form_class_is_none_when_no_form_config(): + crd = Mock(spec=ControlPlaneCRD) + service_def = Mock() + service_def.form_config = None + crd.service_definition = service_def + crd.django_model = Mock() + + if not ( + crd.django_model + and crd.service_definition + and crd.service_definition.form_config + and crd.service_definition.form_config.get("fieldsets") + ): + result = None + else: + result = generate_custom_form_class( + crd.service_definition.form_config, crd.django_model + ) + + assert result is None + + def test_custom_model_form_class_returns_class_when_form_config_exists(): crd = Mock(spec=ControlPlaneCRD) @@ -38,9 +60,18 @@ def test_custom_model_form_class_returns_class_when_form_config_exists(): app_label = "test" crd.django_model = TestModel - result = generate_custom_form_class( - crd.service_definition.form_config, crd.django_model - ) + + if not ( + crd.django_model + and crd.service_definition + and crd.service_definition.form_config + and crd.service_definition.form_config.get("fieldsets") + ): + result = None + else: + result = generate_custom_form_class( + crd.service_definition.form_config, crd.django_model + ) assert result is not None assert hasattr(result, "form_config") @@ -1053,43 +1084,3 @@ def test_empty_values_dont_override_default_configs(): assert name_field.max_length == DEFAULT_FIELD_CONFIGS["name"]["max_length"] assert name_field.required is False # Was overridden by explicit False - - -def test_number_field_with_addon_text_roundtrip(): - class TestModel(models.Model): - name = models.CharField(max_length=100) - disk_size = models.IntegerField() - - class Meta: - app_label = "test" - - form_config = { - "fieldsets": [ - { - "fields": [ - { - "type": "text", - "label": "Name", - "controlplane_field_mapping": "name", - "required": True, - }, - { - "type": "number", - "label": "Disk Size", - "controlplane_field_mapping": "disk_size", - "addon_text": "Gi", - }, - ], - } - ] - } - - form_class = generate_custom_form_class(form_config, TestModel) - form = form_class(initial={"name": "test-instance", "disk_size": "25Gi"}) - - assert form.initial["disk_size"] == 25 - form = form_class(data={"name": "test-instance", "disk_size": "25"}) - form.fields["context"].required = False - assert form.is_valid(), f"Form should be valid but has errors: {form.errors}" - nested_data = form.get_nested_data() - assert nested_data["disk_size"] == "25Gi"