Skip to content

Commit 4c64827

Browse files
committed
minimize redudant code in serializers
1 parent b316b74 commit 4c64827

File tree

1 file changed

+117
-121
lines changed

1 file changed

+117
-121
lines changed

netbox_acls/api/serializers.py

Lines changed: 117 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ..constants import ACL_HOST_ASSIGNMENT_MODELS, ACL_INTERFACE_ASSIGNMENT_MODELS
1717
from ..models import (
1818
AccessList,
19+
BaseACLRule,
1920
ACLExtendedRule,
2021
ACLInterfaceAssignment,
2122
ACLStandardRule,
@@ -44,6 +45,27 @@
4445
ERROR_MESSAGE_ACL_TYPE = "Provided parent Access List is not of right type."
4546

4647

48+
def validate_gfk(data):
49+
"""
50+
Check that the GFK object is valid.
51+
"""
52+
# TODO: This can removed after https://github.com/netbox-community/netbox/issues/10221 is fixed.
53+
try:
54+
assigned_object = data[ # noqa: F841
55+
"assigned_object_type"
56+
].get_object_for_this_type(
57+
id=data["assigned_object_id"],
58+
)
59+
except ObjectDoesNotExist as e:
60+
error_message_invalid_gfk = f"Invalid assigned_object {data['assigned_object_type']} ID {data['assigned_object_id']}"
61+
raise serializers.ValidationError(
62+
{
63+
"assigned_object_type": [error_message_invalid_gfk],
64+
"assigned_object_id": [error_message_invalid_gfk],
65+
}
66+
) from e
67+
68+
4769
class AccessListSerializer(NetBoxModelSerializer):
4870
"""
4971
Defines the serializer for the django AccessList model & associates it to a view.
@@ -97,35 +119,21 @@ def get_assigned_object(self, obj):
97119
def validate(self, data):
98120
"""
99121
Validates api inputs before processing:
100-
- Check that the GFK object is valid.
101-
- Check if Access List has no existing rules before change the Access List's type.
122+
- Check that the GFK object is valid.
123+
- Check if Access List has no existing rules before change the Access List's type.
102124
"""
103-
error_message = {}
104-
105125
# Check that the GFK object is valid.
106-
if "assigned_object_type" in data and "assigned_object_id" in data:
107-
# TODO: This can removed after https://github.com/netbox-community/netbox/issues/10221 is fixed.
108-
try:
109-
assigned_object = data[ # noqa: F841
110-
"assigned_object_type"
111-
].get_object_for_this_type(
112-
id=data["assigned_object_id"],
113-
)
114-
except ObjectDoesNotExist:
115-
# Sets a standard error message for invalid GFK
116-
error_message_invalid_gfk = f"Invalid assigned_object {data['assigned_object_type']} ID {data['assigned_object_id']}"
117-
error_message["assigned_object_type"] = [error_message_invalid_gfk]
118-
error_message["assigned_object_id"] = [error_message_invalid_gfk]
126+
assigned_object = validate_gfk(data)
119127

120128
# Check if Access List has no existing rules before change the Access List's type.
121129
if (
122130
self.instance
123131
and self.instance.type != data.get("type")
124132
and self.instance.rule_count > 0
125133
):
126-
error_message["type"] = [
127-
"This ACL has ACL rules associated, CANNOT change ACL type.",
128-
]
134+
raise serializers.ValidationError(
135+
{"type": ["This ACL has ACL rules associated, CANNOT change ACL type."]}
136+
)
129137

130138
if error_message:
131139
raise serializers.ValidationError(error_message)
@@ -180,66 +188,63 @@ def get_assigned_object(self, obj):
180188
context = {"request": self.context["request"]}
181189
return serializer(obj.assigned_object, context=context).data
182190

191+
def _validate_get_interface_host(self, data, assigned_object):
192+
"""
193+
Check that the associated interface's parent host has the selected ACL defined.
194+
"""
195+
MODEL_MAPPING = {
196+
"interface": "device",
197+
"vminterface": "virtual_machine",
198+
}
199+
200+
assigned_object_model = data["assigned_object_type"].model
201+
202+
return getattr(assigned_object, MODEL_MAPPING.get(assigned_object_model, None))
203+
204+
def _validate_acl_host(self, acl_host, interface_host):
205+
"""
206+
Check that the associated interface's parent host has the selected ACL defined.
207+
"""
208+
if acl_host == interface_host:
209+
return {}
210+
211+
error_acl_not_assigned_to_host = (
212+
"Access List not present on the selected interface's host."
213+
)
214+
return {
215+
"access_list": [error_acl_not_assigned_to_host],
216+
"assigned_object_id": [error_acl_not_assigned_to_host],
217+
}
218+
183219
def validate(self, data):
184220
"""
185221
Validate the AccessList django model's inputs before allowing it to update the instance.
186-
- Check that the GFK object is valid.
187-
- Check that the associated interface's parent host has the selected ACL defined.
222+
- Check that the GFK object is valid.
223+
- Check that the associated interface's parent host has the selected ACL defined.
188224
"""
225+
226+
# Check that the GFK object is valid.
227+
assigned_object = validate_gfk(data)
228+
189229
error_message = {}
190230
acl_host = data["access_list"].assigned_object
191231

192-
# Check that the GFK object is valid.
193-
if "assigned_object_type" in data and "assigned_object_id" in data:
194-
# TODO: This can removed after https://github.com/netbox-community/netbox/issues/10221 is fixed.
195-
try:
196-
assigned_object = data[ # noqa: F841
197-
"assigned_object_type"
198-
].get_object_for_this_type(
199-
id=data["assigned_object_id"],
200-
)
201-
except ObjectDoesNotExist:
202-
# Sets a standard error message for invalid GFK
203-
error_message_invalid_gfk = f"Invalid assigned_object {data['assigned_object_type']} ID {data['assigned_object_id']}"
204-
error_message["assigned_object_type"] = [error_message_invalid_gfk]
205-
error_message["assigned_object_id"] = [error_message_invalid_gfk]
206-
207-
if data["assigned_object_type"].model == "interface":
208-
interface_host = (
209-
data["assigned_object_type"]
210-
.get_object_for_this_type(id=data["assigned_object_id"])
211-
.device
212-
)
213-
elif data["assigned_object_type"].model == "vminterface":
214-
interface_host = (
215-
data["assigned_object_type"]
216-
.get_object_for_this_type(id=data["assigned_object_id"])
217-
.virtual_machine
218-
)
219-
else:
220-
interface_host = None
221-
# Check that the associated interface's parent host has the selected ACL defined.
222-
if acl_host != interface_host:
223-
error_acl_not_assigned_to_host = (
224-
"Access List not present on the selected interface's host."
225-
)
226-
error_message["access_list"] = [error_acl_not_assigned_to_host]
227-
error_message["assigned_object_id"] = [error_acl_not_assigned_to_host]
232+
interface_host = self._validate_get_interface_host(data, assigned_object)
233+
acl_host = data["access_list"].assigned_object
234+
235+
error_message |= self._validate_acl_host(acl_host, interface_host)
228236

229237
if error_message:
230238
raise serializers.ValidationError(error_message)
231239

232240
return super().validate(data)
233241

234242

235-
class ACLStandardRuleSerializer(NetBoxModelSerializer):
243+
class BaseACLRuleSerializer(NetBoxModelSerializer):
236244
"""
237-
Defines the serializer for the django ACLStandardRule model & associates it to a view.
245+
Defines the serializer for the django BaseACLRule model & associates it to a view.
238246
"""
239247

240-
url = serializers.HyperlinkedIdentityField(
241-
view_name="plugins-api:netbox_acls-api:aclstandardrule-detail",
242-
)
243248
access_list = NestedAccessListSerializer()
244249
source_prefix = NestedPrefixSerializer(
245250
required=False,
@@ -249,10 +254,11 @@ class ACLStandardRuleSerializer(NetBoxModelSerializer):
249254

250255
class Meta:
251256
"""
252-
Associates the django model ACLStandardRule & fields to the serializer.
257+
Associates the django model BaseACLRule & fields to the serializer.
253258
"""
254259

255-
model = ACLStandardRule
260+
abstract = True
261+
model = BaseACLRule
256262
fields = (
257263
"id",
258264
"url",
@@ -271,9 +277,9 @@ class Meta:
271277

272278
def validate(self, data):
273279
"""
274-
Validate the ACLStandardRule django model's inputs before allowing it to update the instance:
275-
- Check if action set to remark, but no remark set.
276-
- Check if action set to remark, but source_prefix set.
280+
Validate the BaseACLRule django model's inputs before allowing it to update the instance:
281+
- Check if action set to remark, but no remark set.
282+
- Check if action set to remark, but source_prefix set.
277283
"""
278284
error_message = {}
279285

@@ -292,45 +298,46 @@ def validate(self, data):
292298
return super().validate(data)
293299

294300

295-
class ACLExtendedRuleSerializer(NetBoxModelSerializer):
301+
class ACLStandardRuleSerializer(BaseACLRuleSerializer):
302+
"""
303+
Defines the serializer for the django ACLStandardRule model & associates it to a view.
304+
"""
305+
306+
url = serializers.HyperlinkedIdentityField(
307+
view_name="plugins-api:netbox_acls-api:aclstandardrule-detail",
308+
)
309+
310+
class Meta(BaseACLRuleSerializer.Meta):
311+
"""
312+
Associates the django model ACLStandardRule & fields to the serializer.
313+
"""
314+
315+
model = ACLStandardRule
316+
317+
318+
class ACLExtendedRuleSerializer(BaseACLRuleSerializer):
296319
"""
297320
Defines the serializer for the django ACLExtendedRule model & associates it to a view.
298321
"""
299322

300323
url = serializers.HyperlinkedIdentityField(
301324
view_name="plugins-api:netbox_acls-api:aclextendedrule-detail",
302325
)
303-
access_list = NestedAccessListSerializer()
304-
source_prefix = NestedPrefixSerializer(
305-
required=False,
306-
allow_null=True,
307-
default=None,
308-
)
309326
destination_prefix = NestedPrefixSerializer(
310327
required=False,
311328
allow_null=True,
312329
default=None,
313330
)
314331

315-
class Meta:
332+
class Meta(BaseACLRuleSerializer.Meta):
316333
"""
317334
Associates the django model ACLExtendedRule & fields to the serializer.
318335
"""
319336

320337
model = ACLExtendedRule
321-
fields = (
322-
"id",
323-
"url",
324-
"display",
325-
"access_list",
326-
"index",
327-
"action",
328-
"tags",
329-
"description",
330-
"created",
331-
"custom_fields",
332-
"last_updated",
333-
"source_prefix",
338+
339+
# Add the additional fields to the serializer to support Extended ACL Rules.
340+
fields = BaseACLRuleSerializer.Meta.fields + (
334341
"source_ports",
335342
"destination_prefix",
336343
"destination_ports",
@@ -341,44 +348,33 @@ class Meta:
341348
def validate(self, data):
342349
"""
343350
Validate the ACLExtendedRule django model's inputs before allowing it to update the instance:
344-
- Check if action set to remark, but no remark set.
345-
- Check if action set to remark, but source_prefix set.
346-
- Check if action set to remark, but source_ports set.
347-
- Check if action set to remark, but destination_prefix set.
348-
- Check if action set to remark, but destination_ports set.
349-
- Check if action set to remark, but protocol set.
350-
- Check if action set to remark, but protocol set.
351+
- Check if action set to remark, but no remark set.
352+
- Check if action set to remark, but source_prefix set.
353+
- Check if action set to remark, but source_ports set.
354+
- Check if action set to remark, but destination_prefix set.
355+
- Check if action set to remark, but destination_ports set.
356+
- Check if action set to remark, but protocol set.
357+
- Check if action set to remark, but protocol set.
351358
"""
352359
error_message = {}
360+
rule_attributes = [
361+
"source_prefix",
362+
"source_ports",
363+
"destination_prefix",
364+
"destination_ports",
365+
"protocol",
366+
]
353367

354368
# Check if action set to remark, but no remark set.
355369
if data.get("action") == "remark" and data.get("remark") is None:
356370
error_message["remark"] = [ERROR_MESSAGE_NO_REMARK]
357-
# Check if action set to remark, but source_prefix set.
358-
if data.get("source_prefix"):
359-
error_message["source_prefix"] = [
360-
ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET,
361-
]
362-
# Check if action set to remark, but source_ports set.
363-
if data.get("source_ports"):
364-
error_message["source_ports"] = [
365-
"Action is set to remark, Source Ports CANNOT be set.",
366-
]
367-
# Check if action set to remark, but destination_prefix set.
368-
if data.get("destination_prefix"):
369-
error_message["destination_prefix"] = [
370-
"Action is set to remark, Destination Prefix CANNOT be set.",
371-
]
372-
# Check if action set to remark, but destination_ports set.
373-
if data.get("destination_ports"):
374-
error_message["destination_ports"] = [
375-
"Action is set to remark, Destination Ports CANNOT be set.",
376-
]
377-
# Check if action set to remark, but protocol set.
378-
if data.get("protocol"):
379-
error_message["protocol"] = [
380-
"Action is set to remark, Protocol CANNOT be set.",
381-
]
371+
372+
# Check if action set to remark, but other fields set.
373+
for attribute in rule_attributes:
374+
if data.get(attribute):
375+
error_message[attribute] = [
376+
f'Action is set to remark, {attribute.replace("_", " ").title()} CANNOT be set.'
377+
]
382378

383379
if error_message:
384380
raise serializers.ValidationError(error_message)

0 commit comments

Comments
 (0)