@@ -131,6 +131,53 @@ def to_representation(self, obj):
131131 return obj
132132
133133
134+ class CamelCaseJSONField (JSONFieldSerializer ):
135+ """JSONField that stores camelCase data and accepts snake_case input.
136+
137+ - Input (API -> DB): snake_case keys -> camelCase (before validation)
138+ - Output (DB -> API): camelCase keys -> snake_case
139+ - Storage: camelCase (Python code & K8s API unchanged)
140+ """
141+
142+ @staticmethod
143+ def camel_to_snake (s ):
144+ """camelCase -> snake_case"""
145+ s1 = re .sub (r'(.)([A-Z][a-z]+)' , r'\1_\2' , s )
146+ return re .sub (r'([a-z0-9])([A-Z])' , r'\1_\2' , s1 ).lower ()
147+
148+ @staticmethod
149+ def snake_to_camel (s ):
150+ """snake_case -> camelCase"""
151+ parts = s .split ('_' )
152+ return parts [0 ] + '' .join (x .title () for x in parts [1 :])
153+
154+ @classmethod
155+ def underscoreize (cls , data ):
156+ """Recursively convert dict keys: camelCase -> snake_case"""
157+ if isinstance (data , dict ):
158+ return {cls .camel_to_snake (k ): cls .underscoreize (v ) for k , v in data .items ()}
159+ if isinstance (data , list ):
160+ return [cls .underscoreize (i ) for i in data ]
161+ return data
162+
163+ @classmethod
164+ def camelize (cls , data ):
165+ """Recursively convert dict keys: snake_case -> camelCase"""
166+ if isinstance (data , dict ):
167+ return {cls .snake_to_camel (k ): cls .camelize (v ) for k , v in data .items ()}
168+ if isinstance (data , list ):
169+ return [cls .camelize (i ) for i in data ]
170+ return data
171+
172+ def to_internal_value (self , data ):
173+ val = super ().to_internal_value (data )
174+ return self .camelize (val )
175+
176+ def to_representation (self , obj ):
177+ val = super ().to_representation (obj )
178+ return self .underscoreize (val )
179+
180+
134181class AuthSerializer (serializers .Serializer ):
135182 username = serializers .CharField (
136183 label = gettext_lazy ("Username" ),
@@ -283,10 +330,10 @@ class ConfigSerializer(serializers.ModelSerializer):
283330 app = serializers .SlugRelatedField (slug_field = 'id' , queryset = models .app .App .objects .all ())
284331 values = JSONFieldSerializer (required = False , binary = True )
285332 limits = JSONFieldSerializer (required = False , binary = True )
286- lifecycle = JSONFieldSerializer (convert_to_str = False , required = False , binary = True )
333+ lifecycle = CamelCaseJSONField (convert_to_str = False , required = False , binary = True )
287334 tags = JSONFieldSerializer (required = False , binary = True )
288335 registry = JSONFieldSerializer (required = False , binary = True )
289- healthcheck = JSONFieldSerializer (convert_to_str = False , required = False , binary = True )
336+ healthcheck = CamelCaseJSONField (convert_to_str = False , required = False , binary = True )
290337 routable = serializers .BooleanField (required = False )
291338 termination_grace_period = JSONFieldSerializer (required = False , binary = True )
292339
@@ -509,12 +556,15 @@ class ServiceSerializer(serializers.ModelSerializer):
509556 protocol = serializers .CharField (required = True )
510557 target_port = serializers .IntegerField (default = DEFAULT_CONTAINER_PORT )
511558 ptype = serializers .CharField (required = True )
559+ name = serializers .ReadOnlyField ()
560+ domain = serializers .ReadOnlyField ()
561+ ports = CamelCaseJSONField ()
512562
513563 class Meta :
514564 """Metadata options for a :class:`ServiceSerializer`."""
515565 model = models .service .Service
516- fields = ['created' , 'updated' , 'app' , 'ptype' ]
517- read_only_fields = ['uuid ' ]
566+ fields = ['created' , 'updated' , 'app' , 'ptype' , 'name' , 'domain' , 'ports' ]
567+ read_only_fields = ['created' , 'updated' , 'name' , 'domain' , 'ports ' ]
518568
519569 @staticmethod
520570 def validate_protocol (value ):
@@ -566,6 +616,34 @@ def to_representation(self, obj):
566616 return obj
567617
568618
619+ class PodDetailSerializer (serializers .Serializer ):
620+ """Serialize pod detail data from Kubernetes API."""
621+ container = serializers .CharField ()
622+ image = serializers .CharField ()
623+ command = serializers .JSONField ()
624+ args = serializers .JSONField ()
625+ state = CamelCaseJSONField ()
626+ last_state = CamelCaseJSONField ()
627+ ready = serializers .BooleanField ()
628+ restart_count = serializers .IntegerField ()
629+ status = serializers .CharField ()
630+ reason = serializers .CharField ()
631+ message = serializers .CharField ()
632+
633+
634+ class DeploymentDetailSerializer (serializers .Serializer ):
635+ """Serialize deployment detail data from Kubernetes API."""
636+ container = serializers .CharField ()
637+ image = serializers .CharField ()
638+ command = serializers .JSONField ()
639+ args = serializers .JSONField ()
640+ liveness_probe = CamelCaseJSONField ()
641+ readiness_probe = CamelCaseJSONField ()
642+ limits = serializers .JSONField ()
643+ volume_mounts = CamelCaseJSONField ()
644+ node_selector = serializers .JSONField ()
645+
646+
569647class EventSerializer (serializers .BaseSerializer ):
570648 reason = serializers .CharField ()
571649 message = serializers .CharField ()
@@ -626,7 +704,7 @@ class VolumeSerializer(serializers.ModelSerializer):
626704
627705 app = serializers .SlugRelatedField (slug_field = 'id' , queryset = models .app .App .objects .all ())
628706 path = JSONFieldSerializer (required = False , binary = True )
629- parameters = serializers . JSONField (required = False )
707+ parameters = CamelCaseJSONField (required = False )
630708
631709 class Meta :
632710 """Metadata options for a :class:`AppVolumeSerializer`."""
@@ -717,7 +795,7 @@ class GatewaySerializer(serializers.ModelSerializer):
717795
718796 class Meta :
719797 model = models .gateway .Gateway
720- fields = '__all__'
798+ fields = [ 'app' , 'name' , 'ports' , 'addresses' , 'created' , 'updated' ]
721799
722800 validate_name = staticmethod (validate_name )
723801
@@ -756,14 +834,14 @@ class RouteSerializer(serializers.ModelSerializer):
756834 app = serializers .SlugRelatedField (slug_field = 'id' , queryset = models .app .App .objects .all ())
757835 kind = serializers .CharField (max_length = 15 , required = True )
758836 name = serializers .CharField (max_length = 63 , required = True )
759- rules = serializers . JSONField ( required = True )
837+ rules = CamelCaseJSONField ( convert_to_str = False , required = True )
760838 routable = serializers .BooleanField (read_only = True )
761839 parent_refs = serializers .JSONField (required = True )
762840
763841 class Meta :
764842 """Metadata options for a :class:`RouteSerializer`."""
765843 model = models .gateway .Route
766- fields = '__all__'
844+ fields = [ 'app' , 'kind' , 'name' , 'rules' , 'routable' , 'parent_refs' , 'created' , 'updated' ]
767845
768846 validate_name = staticmethod (validate_name )
769847
@@ -843,35 +921,24 @@ class Meta:
843921 read_only_fields = ['id' , 'token' , 'created' ]
844922
845923
846- class AddonClassSerializer (serializers .BaseSerializer ):
924+ class AddonClassSerializer (serializers .Serializer ):
847925 """Serialize AddonClass data from Kubernetes CRD."""
848-
849- name = serializers .CharField (required = False )
850- description = serializers .CharField (required = False )
851- kind = serializers .CharField (required = False )
852- storage_model = serializers .CharField (required = False )
853- plans = serializers .ListField (required = False )
854-
855- def to_representation (self , instance ):
856- spec = instance .get ('spec' , {})
857- target_resource = spec .get ('targetResource' , {})
858- return {
859- 'name' : instance .get ('metadata' , {}).get ('name' , '' ),
860- 'description' : spec .get ('description' , '' ),
861- 'kind' : target_resource .get ('kind' , '' ),
862- 'storage_model' : spec .get ('storageModel' , '' ),
863- 'plans' : spec .get ('plans' , []),
864- }
926+ name = serializers .CharField (source = 'metadata.name' , required = False )
927+ description = serializers .CharField (source = 'spec.description' , required = False )
928+ kind = serializers .CharField (source = 'spec.targetResource.kind' , required = False )
929+ storage_model = serializers .CharField (source = 'spec.storageModel' , required = False )
930+ plans = CamelCaseJSONField (source = 'spec.plans' , required = False )
865931
866932
867933class AddonInstanceSerializer (serializers .ModelSerializer ):
868934 """Serialize AddonInstance model."""
869935 app = serializers .ReadOnlyField (source = 'app.id' )
870936 name = serializers .CharField (max_length = 63 , required = False )
871937 kind = serializers .CharField (max_length = 63 , required = False )
938+ parameters = CamelCaseJSONField (convert_to_str = False , default = dict , required = False )
872939
873940 class Meta :
874941 model = models .addon .AddonInstance
875- fields = ['uuid' , ' name' , 'app' , 'plan' , 'kind' ,
942+ fields = ['name' , 'app' , 'plan' , 'kind' ,
876943 'multiplier' , 'parameters' , 'created' , 'updated' ]
877- read_only_fields = ['uuid' , ' multiplier' , 'created' , 'updated' ]
944+ read_only_fields = ['multiplier' , 'created' , 'updated' ]
0 commit comments