Coverage for /home/runner/work/netbox-InterfaceNameRules-plugin/netbox-InterfaceNameRules-plugin/netbox-InterfaceNameRules-plugin/netbox_interface_name_rules/api/serializers.py: 100%

18 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-22 21:16 +0000

1# SPDX-License-Identifier: Apache-2.0 

2# Copyright (C) 2025 Marcin Zieba <marcinpsk@gmail.com> 

3from netbox.api.serializers import NetBoxModelSerializer 

4from rest_framework import serializers 

5 

6from netbox_interface_name_rules.models import InterfaceNameRule 

7 

8 

9class InterfaceNameRuleSerializer(NetBoxModelSerializer): 

10 """Serializer for InterfaceNameRule with mutual-exclusivity validation.""" 

11 

12 class Meta: 

13 model = InterfaceNameRule 

14 fields = [ 

15 "id", 

16 "url", 

17 "display", 

18 "module_type", 

19 "module_type_pattern", 

20 "module_type_is_regex", 

21 "parent_module_type", 

22 "device_type", 

23 "platform", 

24 "name_template", 

25 "parent_name_template", 

26 "breakout_mode", 

27 "channel_count", 

28 "channel_start", 

29 "description", 

30 "enabled", 

31 "applies_to_device_interfaces", 

32 "tags", 

33 "custom_fields", 

34 "created", 

35 "last_updated", 

36 ] 

37 brief_fields = ["id", "url", "display", "name_template", "description"] 

38 

39 def validate(self, attrs): 

40 """Ensure module_type XOR module_type_pattern depending on regex mode.""" 

41 attrs = super().validate(attrs) 

42 instance = self.instance 

43 

44 # For PATCH requests, fall back to existing instance values for fields not in attrs 

45 is_device_level = attrs.get( 

46 "applies_to_device_interfaces", getattr(instance, "applies_to_device_interfaces", False) 

47 ) 

48 is_regex = attrs.get("module_type_is_regex", getattr(instance, "module_type_is_regex", False)) 

49 module_type = attrs.get("module_type", getattr(instance, "module_type", None)) 

50 pattern = attrs.get("module_type_pattern", getattr(instance, "module_type_pattern", "")) 

51 

52 if is_device_level: 

53 # Device-level rules must not have a module_type FK 

54 if module_type: # pragma: no cover # model.clean() runs first 

55 raise serializers.ValidationError( 

56 {"module_type": "Module type must be empty for device-level interface rules."} 

57 ) 

58 elif is_regex: 

59 if not pattern: # pragma: no cover # model.clean() runs first 

60 raise serializers.ValidationError( 

61 {"module_type_pattern": "Regex pattern is required when regex mode is enabled."} 

62 ) 

63 if module_type: # pragma: no cover # model.clean() runs first 

64 raise serializers.ValidationError( 

65 {"module_type": "Cannot set both module_type and module_type_pattern. Choose one."} 

66 ) 

67 else: 

68 if not module_type: # pragma: no cover # model.clean() runs first 

69 raise serializers.ValidationError( 

70 {"module_type": "module_type is required when regex mode is disabled."} 

71 ) 

72 if pattern: # pragma: no cover # model.clean() runs first 

73 raise serializers.ValidationError( 

74 {"module_type_pattern": "Cannot set module_type_pattern when regex mode is disabled."} 

75 ) 

76 return attrs