Skip to content

feat: ingest RackReservation with match-only user resolution#185

Merged
leoparente merged 5 commits into
developfrom
feat/obs-user-rackreservation
Jul 21, 2026
Merged

feat: ingest RackReservation with match-only user resolution#185
leoparente merged 5 commits into
developfrom
feat/obs-user-rackreservation

Conversation

@leoparente

Copy link
Copy Markdown
Contributor

Summary

Makes dcim.rackreservation ingestable by resolving its required user FK, and
adds match-only resolution for users.user: users are resolved against
existing rows by username and are never created or updated via ingest.
Previously a rack reservation could not be applied at all (Field user is required), because user was not a resolvable reference.

What changes

  • Generated metadata (plugin_utils.py) — updated to include users.user
    (username) in the legal fields and the RackReservation.user reference (plus
    the other restored user references).
  • Transformer — a match-only reference that resolves to an existing user is
    emitted as a pure reference (the parent gets the user's pk; no change node is
    emitted for the user). An unresolved user reference produces a clean
    per-entity deviation instead of a create.
  • Applier — rejects any create/update of a match-only type in _apply_change.
    This covers the direct apply-change-set / bulk-apply path (which bypasses
    the transformer), so a client-supplied changeset cannot create or rename a user.

Why match-only

Auto-creating or renaming Django auth users from discovery/ingest data is a
privilege/security risk (which password? which permissions?). Users must
pre-exist; a reservation referencing an unknown user deviates cleanly rather than
minting one.

Scope note

Enabling users.user as a resolvable reference restores it everywhere it is
referenced — RackReservation.user, Owner.users, JournalEntry.created_by,
and the generic-object *_user variants — all match-only (never created/updated).

Tests (v4.6.x suite green, ruff clean)

  • existing user resolves + reservation applies (user emitted as a pure reference,
    no users.user change; existing user not duplicated)
  • unknown / case-mismatch / missing user → clean per-entity deviation, no row created
  • direct users.user CREATE and UPDATE changesets → rejected by the applier guard
    (username unchanged in the DB)
  • user resolution is idempotent across re-ingests

Merge order

Depends on the ingester proto + SDK changes landing first (the regenerated
plugin_utils.py metadata reflects the new user reference).

leoparente and others added 3 commits July 8, 2026 13:12
Regenerated from drf-extract: adds users.user to legal fields (username) and
the RackReservation.user RefInfo (plus the other restored user references).
Layers on the existing cabling metadata. Full v4.6.x suite: 426 OK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
users.user is a match-only reference type: resolved against existing rows by
username, never created or updated via ingest.

- transformer: _MATCH_ONLY_TYPES guard in _resolve_existing_references. A
  match-only ref that resolves to an existing row is emitted as a pure
  reference (parent gets the pk, no change for the node — a users.user change
  would fail validation anyway since NetBox's User requires a password we
  never carry). An unresolved match-only ref raises a per-entity deviation
  instead of a CREATE.
- applier: reject any CREATE/UPDATE of a match-only type in _apply_change,
  covering the direct apply-change-set / bulk-apply path that bypasses the
  transformer (so a client changeset cannot create or rename a user).

Enables dcim.rackreservation ingest (its required user FK). Tests: existing
user resolves + applies; unknown/case-mismatch/missing user deviate; direct
create/update user changesets rejected; user resolution is idempotent.
Suite: 434 OK; ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- transformer: extract _resolve_by_netbox_id + _mark_seen helpers (keeps
  _resolve_existing_references under the C901 threshold after adding the
  match-only branches) and add the match-only pure-reference short-circuit to
  the netbox_id (PK) resolution path for symmetry with find_existing_object.
- tests: pin the direct-CREATE rejection to the match-only guard (assert the
  message + serializer-valid password); directly assert matched users are
  emitted as pure references (no users.user change); add negative assertions
  to the case-mismatch test. Dropped the PK-match full-NOOP idempotency test:
  it is confounded by dcim.rack having no natural-key match when location is
  null (a pre-existing, unrelated behavior) — the user-resolution idempotency
  it was meant to prove is covered directly instead.

Suite: 434 OK; ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the python label Jul 9, 2026
The v4.6.0 matrix leg (largest suite: cabling + rackreservation + coverage +
image build) was hitting the 10-minute job timeout; develop already runs it at
~9m45s, so it was one test away from failing regardless of this branch. Raise
the timeout to give the growing v4.6.x suite headroom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
6871 6310 92% 0% 🟢

New Files

File Coverage Status
netbox_diode_plugin/tests/v4.6.x/tests/test_user_rackreservation.py 99% 🟢
TOTAL 99% 🟢

Modified Files

File Coverage Status
netbox_diode_plugin/api/applier.py 96% 🟢
netbox_diode_plugin/api/plugin_utils.py 84% 🟢
netbox_diode_plugin/api/transformer.py 93% 🟢
TOTAL 91% 🟢

updated for commit: 3087dc9 by action🐍

@leoparente
leoparente marked this pull request as ready for review July 21, 2026 13:10
@leoparente leoparente self-assigned this Jul 21, 2026
Move the match-only type registry out of transformer as a private
symbol and into common.py as a public constant. It is a shared contract
between the transformer (plan path) and applier (direct-apply path), so
it lives in the module both already depend on rather than one layer
importing the other's private symbol. Unlike plugin_utils.py, common.py
is not generated code, so the constant won't be clobbered by a regen.

Side effect: removes the last applier -> transformer import edge.

No behavior change; v4.6.x suite green (434 tests), ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff3e20db4b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread netbox_diode_plugin/api/transformer.py
@github-actions

Copy link
Copy Markdown

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
6904 6342 92% 0% 🟢

New Files

File Coverage Status
netbox_diode_plugin/tests/v4.6.x/tests/test_user_rackreservation.py 99% 🟢
TOTAL 99% 🟢

Modified Files

File Coverage Status
netbox_diode_plugin/api/applier.py 96% 🟢
netbox_diode_plugin/api/common.py 91% 🟢
netbox_diode_plugin/api/plugin_utils.py 84% 🟢
netbox_diode_plugin/api/transformer.py 93% 🟢
TOTAL 91% 🟢

updated for commit: ff3e20d by action🐍

@leoparente
leoparente merged commit 6dac3cb into develop Jul 21, 2026
8 checks passed
@leoparente
leoparente deleted the feat/obs-user-rackreservation branch July 21, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants