Skip to content

Commit f563b48

Browse files
committed
fix: fix errors attempting value harmonization
1 parent 905e2c8 commit f563b48

File tree

4 files changed

+106
-3
lines changed

4 files changed

+106
-3
lines changed

netbox_diode_plugin/api/applier.py

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ def apply_changeset(change_set: ChangeSet, request) -> ChangeSetResult:
3838
raise _err_from_validation_error(e, f"changes[{i}]")
3939
except ObjectDoesNotExist:
4040
raise _err(f"{object_type} with id {change.object_id} does not exist", f"changes[{i}]", "object_id")
41+
except TypeError as e:
42+
# this indicates a problem in model validation (should raise ValidationError)
43+
# but raised non-validation error (TypeError) -- we don't know which field trigged it.
44+
logger.error(f"invalid data type for unspecified field (validation raised non-validation error): {data}: {e}")
45+
raise _err("invalid data type for field", f"changes[{i}]", "__all__")
4146
# ConstraintViolationError ?
4247
# ...
4348

netbox_diode_plugin/api/differ.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from django.core.exceptions import ValidationError
1111
from utilities.data import shallow_compare_dict
1212

13-
from .common import Change, ChangeSet, ChangeSetException, ChangeSetResult, ChangeType
13+
from .common import Change, ChangeSet, ChangeSetException, ChangeSetResult, ChangeType, UnresolvedReference
1414
from .plugin_utils import get_primary_value, legal_fields
1515
from .supported_models import extract_supported_models
1616
from .transformer import cleanup_unresolved_references, set_custom_field_defaults, transform_proto_json
@@ -84,12 +84,20 @@ def prechange_data_from_instance(instance) -> dict: # noqa: C901
8484

8585
def _harmonize_formats(prechange_data: dict, postchange_data: dict):
8686
for k, v in prechange_data.items():
87+
if k.startswith('_'):
88+
continue
8789
if isinstance(v, datetime.datetime):
8890
prechange_data[k] = v.strftime("%Y-%m-%dT%H:%M:%SZ")
8991
elif isinstance(v, datetime.date):
9092
prechange_data[k] = v.strftime("%Y-%m-%d")
9193
elif isinstance(v, int) and k in postchange_data:
92-
postchange_data[k] = int(postchange_data[k])
94+
val = postchange_data[k]
95+
if isinstance(val, UnresolvedReference):
96+
continue
97+
try:
98+
postchange_data[k] = int(val)
99+
except Exception:
100+
continue
93101
elif isinstance(v, dict):
94102
_harmonize_formats(v, postchange_data.get(k, {}))
95103

netbox_diode_plugin/api/transformer.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,10 @@ def _prepare_custom_fields(object_type: str, custom_fields: dict) -> tuple[dict,
452452
out[key] = value
453453
elif value_type == "date":
454454
# truncate to YYYY-MM-DD
455-
out[key] = datetime.datetime.fromisoformat(value).strftime("%Y-%m-%d")
455+
try:
456+
out[key] = datetime.datetime.fromisoformat(value).strftime("%Y-%m-%d")
457+
except Exception:
458+
out[key] = value
456459
elif value_type == "integer":
457460
out[key] = int(value)
458461
elif value_type == "json":

netbox_diode_plugin/tests/test_api_diff_and_apply.py

+87
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,64 @@ def test_generate_diff_and_apply_create_interface_with_tags(self):
113113
self.assertEqual(new_interface.tags.first().name, "tag 1")
114114

115115

116+
def test_generate_diff_and_apply_create_and_update_device_role(self):
117+
"""Test generate diff and apply create and update device role."""
118+
device_uuid = str(uuid4())
119+
role_1_uuid = str(uuid4())
120+
role_2_uuid = str(uuid4())
121+
site_uuid = str(uuid4())
122+
payload = {
123+
"timestamp": 1,
124+
"object_type": "dcim.device",
125+
"entity": {
126+
"device": {
127+
"name": f"Device {device_uuid}",
128+
"deviceType": {
129+
"model": f"Device Type {uuid4()}",
130+
"manufacturer": {
131+
"name": f"Manufacturer {uuid4()}"
132+
}
133+
},
134+
"role": {
135+
"name": f"Role {role_1_uuid}"
136+
},
137+
"site": {
138+
"name": f"Site {site_uuid}"
139+
}
140+
},
141+
}
142+
}
143+
_, response = self.diff_and_apply(payload)
144+
new_device = Device.objects.get(name=f"Device {device_uuid}")
145+
self.assertEqual(new_device.site.name, f"Site {site_uuid}")
146+
self.assertEqual(new_device.role.name, f"Role {role_1_uuid}")
147+
payload = {
148+
"timestamp": 1,
149+
"object_type": "dcim.device",
150+
"entity": {
151+
"device": {
152+
"name": f"Device {device_uuid}",
153+
"deviceType": {
154+
"model": f"Device Type {uuid4()}",
155+
"manufacturer": {
156+
"name": f"Manufacturer {uuid4()}"
157+
}
158+
},
159+
"role": {
160+
"name": f"Role {role_2_uuid}"
161+
},
162+
"site": {
163+
"name": f"Site {site_uuid}"
164+
}
165+
},
166+
}
167+
}
168+
_, response = self.diff_and_apply(payload)
169+
device = Device.objects.get(name=f"Device {device_uuid}")
170+
self.assertEqual(device.site.name, f"Site {site_uuid}")
171+
self.assertEqual(device.role.name, f"Role {role_2_uuid}")
172+
173+
116174
def test_generate_diff_and_apply_create_site_autoslug(self):
117175
"""Test generate diff and apply create site."""
118176
"""Test generate diff create site."""
@@ -321,6 +379,35 @@ def test_generate_diff_and_apply_create_and_update_site_with_custom_field(self):
321379
diff = response1.json().get("change_set", {})
322380
self.assertEqual(diff.get("changes", []), [])
323381

382+
def test_generate_diff_wrong_type_date(self):
383+
"""Test generate diff wrong type date."""
384+
payload = {
385+
"timestamp": 1,
386+
"object_type": "dcim.site",
387+
"entity": {
388+
"site": {
389+
"name": "Site Generate Diff 1",
390+
"slug": "site-generate-diff-1",
391+
"customFields": {
392+
"mydate": {
393+
"date": 12,
394+
},
395+
},
396+
},
397+
}
398+
}
399+
response1 = self.client.post(
400+
self.diff_url, data=payload, format="json", **self.user_header
401+
)
402+
self.assertEqual(response1.status_code, status.HTTP_200_OK)
403+
404+
diff = response1.json().get("change_set", {})
405+
406+
response2 = self.client.post(
407+
self.apply_url, data=diff, format="json", **self.user_header
408+
)
409+
self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST)
410+
324411

325412
def diff_and_apply(self, payload):
326413
"""Diff and apply the payload."""

0 commit comments

Comments
 (0)