Skip to content

fix: preserve explicit empty values and null refs in update changesets#162

Open
mfiedorowicz wants to merge 2 commits intodevelopfrom
fix/preserve-explicit-empty-values-in-changeset
Open

fix: preserve explicit empty values and null refs in update changesets#162
mfiedorowicz wants to merge 2 commits intodevelopfrom
fix/preserve-explicit-empty-values-in-changeset

Conversation

@mfiedorowicz
Copy link
Copy Markdown
Member

@mfiedorowicz mfiedorowicz commented Apr 15, 2026

Summary

Related to netboxlabs/diode#421

  • Differ: for UPDATE changesets, stop stripping empty strings ("") and None values from changeset data. This allows users to explicitly clear optional fields (e.g. reset description to "" or remove a tenant assignment) via diode ingestion. CREATE changesets retain existing behavior (strip empty values to avoid noise).
  • Transformer: handle null and {} (empty dict) values for reference fields. An empty dict comes from protojson when the SDK sets a message field to an empty message (e.g. Tenant{}) to signal "clear this field". Both are treated as "clear this FK" for regular FKs, generic FKs (e.g. assigned_object), and circular reference fields.

Background

Previously, clean_diff_data() stripped empty strings, None, empty lists, and empty dicts unconditionally. This made three scenarios indistinguishable:

User intent Proto / JSON representation Previous behavior New behavior
Leave field unchanged Key absent from JSON Correct (not in changeset) Correct (unchanged)
Clear string field "description": "" Stripped — treated as "leave unchanged" Preserved in update data
Clear FK reference "tenant": null or "tenant": {} Stripped or crash Converted to None, preserved

A companion test in diode-server (branch test/protojson-field-presence-behavior) confirms that protoToJSON() already emits the correct signals — no Go server changes needed:

  • optional string not set → omitted; set to """description": ""
  • optional Tenant nil → omitted; set to Tenant{}"tenant": {}
  • oneof not set → all variants omitted; set to Interface{}"assigned_object_interface": {}

Test plan

  • 297 tests pass (291 existing + 15 new covering all field-clearing scenarios)
  • Verify with a real diode ingestion flow (SDK → server → plugin) that clearing a field works end-to-end
  • SDK changes to document/support the empty-message convention for clearing FKs (separate PR)

… update changesets

Previously, clean_diff_data() stripped empty strings, None values, and
empty dicts from changeset data unconditionally. This made it impossible
for users to clear optional fields (e.g. reset description to "" or
remove a tenant assignment) via diode ingestion — the "clear" signal
was indistinguishable from "field not provided".

Differ: for UPDATE changesets, stop stripping empty values from the data
dict so that explicitly-provided empty strings and None values survive
into the changeset. CREATE changesets retain the existing behavior.

Transformer: handle null and empty-dict ({}) values for reference fields.
An empty dict comes from protojson when the SDK sets a message field to
an empty message (e.g. Tenant{}) to signal "clear this field". Both null
and {} are now treated as "clear this FK" for regular FKs, generic FKs,
and circular reference fields.
@mfiedorowicz mfiedorowicz self-assigned this Apr 15, 2026
@github-actions
Copy link
Copy Markdown

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
4957 4516 91% 0% 🟢

New Files

No new covered files...

Modified Files

File Coverage Status
netbox_diode_plugin/api/differ.py 95% 🟢
netbox_diode_plugin/api/transformer.py 94% 🟢
netbox_diode_plugin/tests/v4.5.x/tests/test_api_generate_diff.py 99% 🟢
TOTAL 96% 🟢

updated for commit: c41bf8e by action🐍

@mfiedorowicz mfiedorowicz requested a review from ldrozdz93 April 15, 2026 08:53
Copy link
Copy Markdown

@ldrozdz93 ldrozdz93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
Just to note, IIUC this does not cover clearing custom_fields or tags. I can easily imagine customers wanting to keep their netbox device or networks tags in sync with their infra, including the removal of old tags.

@ldrozdz93
Copy link
Copy Markdown

For the record: I run that locally and it looks fine. Interface assigned to an IP was properly removed from that IP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants