Disk size SI units #302

Merged
tobru merged 3 commits from 287-disk-size-unit into main 2025-11-20 14:59:19 +00:00
3 changed files with 67 additions and 37 deletions
Showing only changes of commit de6794046d - Show all commits

View file

@ -239,9 +239,9 @@ The Servala Team"""
service_offering = ServiceOffering.objects.get( service_offering = ServiceOffering.objects.get(
osb_plan_id=plan_id, service=service osb_plan_id=plan_id, service=service
) )
except Service.DoesNotExist: except Service.DoesNotExist: # pragma: no-cover
return self._error(f"Unknown service_id: {service_id}") return self._error(f"Unknown service_id: {service_id}")
except ServiceOffering.DoesNotExist: except ServiceOffering.DoesNotExist: # pragma: no-cover
return self._error( return self._error(
f"Unknown plan_id: {plan_id} for service_id: {service_id}" f"Unknown plan_id: {plan_id} for service_id: {service_id}"
) )
@ -284,7 +284,7 @@ The Servala Team"""
if service_instance: if service_instance:
organization = service_instance.organization organization = service_instance.organization
except Exception: except Exception: # pragma: no cover
pass pass
description_parts = [f"Action: {action}", f"Service: {service.name}"] description_parts = [f"Action: {action}", f"Service: {service.name}"]

View file

@ -1,3 +1,5 @@
from contextlib import suppress
from django import forms from django import forms
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.forms.models import ModelForm, ModelFormMetaclass from django.forms.models import ModelForm, ModelFormMetaclass
@ -335,6 +337,19 @@ class CustomFormMixin(FormGeneratorMixin):
if field_type == "number": if field_type == "number":
min_val = field_config.get("min_value") min_val = field_config.get("min_value")
max_val = field_config.get("max_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 = [] validators = []
if min_val is not None: if min_val is not None:
@ -406,6 +421,11 @@ class CustomFormMixin(FormGeneratorMixin):
mapping = field_name mapping = field_name
value = self.cleaned_data.get(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(".") parts = mapping.split(".")
current = nested current = nested
for part in parts[:-1]: for part in parts[:-1]:

View file

@ -8,28 +8,7 @@ from servala.core.crd import generate_custom_form_class
from servala.core.crd.forms import DEFAULT_FIELD_CONFIGS, MANDATORY_FIELDS from servala.core.crd.forms import DEFAULT_FIELD_CONFIGS, MANDATORY_FIELDS
from servala.core.forms import ServiceDefinitionAdminForm from servala.core.forms import ServiceDefinitionAdminForm
from servala.core.models import ControlPlaneCRD from servala.core.models import ControlPlaneCRD
from servala.frontend.forms.widgets import NumberInputWithAddon
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(): def test_custom_model_form_class_returns_class_when_form_config_exists():
@ -60,18 +39,9 @@ def test_custom_model_form_class_returns_class_when_form_config_exists():
app_label = "test" app_label = "test"
crd.django_model = TestModel crd.django_model = TestModel
result = generate_custom_form_class(
if not ( crd.service_definition.form_config, crd.django_model
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 result is not None
assert hasattr(result, "form_config") assert hasattr(result, "form_config")
@ -1084,3 +1054,43 @@ def test_empty_values_dont_override_default_configs():
assert name_field.max_length == DEFAULT_FIELD_CONFIGS["name"]["max_length"] assert name_field.max_length == DEFAULT_FIELD_CONFIGS["name"]["max_length"]
assert name_field.required is False # Was overridden by explicit False 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"