Skip to content

Consolidate the mapping and rule menus into two tabbed sidebar entries - #122

Merged
marcinpsk merged 10 commits into
developfrom
feat/nav-consolidation
Aug 10, 2026
Merged

Consolidate the mapping and rule menus into two tabbed sidebar entries#122
marcinpsk merged 10 commits into
developfrom
feat/nav-consolidation

Conversation

@marcinpsk

@marcinpsk marcinpsk commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

Collapse the plugin sidebar's nine per-model Mappings items (five type mappings + four rule/pattern engines) into two single entries — Mappings and Rules & Patterns. The individual object lists now cross-link through a switcher-pills row rendered above the native Results/Filters tabs, so each tab is the model's real ObjectListView page (not an embedded pane) and keeps the full generic feature set (filters, bulk ops, YAML import/export, changelog). The active pill is announced to screen readers with aria-current="page".

This description covers only the delta over feat/bulk-import (#115) — the three tip commits below. The GitHub diff shows the parent-branch commits too until they merge; review this PR against feat/bulk-import.

  • feat(nav): collapse the mapping/rule menus into the two tabbed entries; move the per-model menu buttons off the sidebar (each list page already carries its own Add/Import controls).
  • fix(nav): announce the active switcher pill with aria-current="page" (Bootstrap nav-pills a11y) on both switchers.
  • test(nav): assert the active pill by attributes (class + href + exactly-one aria-current="page"), decoupled from exact attribute order.

Motivation / Problem

  • Feature + Refactor / cleanup.

Nine sidebar items crowded the plugin menu. Grouping them into two entries with an in-page switcher keeps every list fully functional while decluttering the nav. Convention going forward (enforced by the navigation tests): a branch that introduces a new rule/pattern model adds its switcher tab in the same commit that adds the model — feat/parent-child-interfaces (Port Stack LAG Patterns) and feat/serial-ports (Serial Sensor Types) already follow it up-stack.

Scope of Change

  • Web UI / templates
  • Tests
  • Docs only (menu-path references)

How Was This Tested?

  • Unit tests: yes — menu-structure tests (two entries, no per-model items) plus rendered-switcher e2e tests (real client → view → template) in test_rules_patterns_navigation.py, asserting each tab renders the real list page and that exactly one aria-current="page" pill is announced per page. Red → green on the a11y change.
  • Manual testing: yes — verified live against the dev server: the sidebar shows the two entries, the switcher pills render above Results/Filters, and each pill lands on its real list page with filters/bulk-ops/import-export/changelog intact.

Manual Test Steps

  1. Open the plugin sidebar → confirm only Mappings and Rules & Patterns appear (not the previous nine items).
  2. Open a Mappings list → confirm the switcher-pills row above the Results/Filters tabs; the current list's pill carries aria-current="page".
  3. Click through the pills → each opens the real ObjectListView (filters, bulk operations, import/export, changelog all present).

Risk Assessment

  • Affects existing users? Navigation layout only — the Mappings group changes from nine sidebar items to two, with an added in-page switcher. No behavioural change to sync/import.
  • Could cause unintended imports / updates? No — no sync/import, ORM, LibreNMS API, config, or migration code is touched.

Backwards Compatibility

  • No breaking changes — model URLs and views are unchanged; only the sidebar grouping and a cross-link row are added.

Other Notes

Stacked on feat/bulk-import (#115). The only delta this PR adds over that base is the navigation consolidation described above (navigation.py, the _mapping_tabs.html / _rules_patterns_tabs.html switcher includes, the tab row added to the eight list templates, test_rules_patterns_navigation.py, and the two docs menu-path updates).

Summary by CodeRabbit

  • New Features

    • Added bulk import collision detection with detailed device and virtual machine information.
    • Bulk imports now support cancellation-aware checks, unresolved-row skipping, and clearer validation outcomes.
    • Consolidated Mappings and Rules & Patterns navigation with tabbed list pages and bulk YAML import/export.
  • Bug Fixes

    • Improved permission enforcement across synchronization, migration, and import actions.
    • Prevented stale or mismatched cached data from affecting imports.
    • Added warnings for hidden serial conflicts and improved VLAN synchronization handling.
  • Documentation

    • Updated mapping and rules guidance for the consolidated navigation and available actions.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4350e592-eb3f-4b3c-b444-2cbda9255efc

📥 Commits

Reviewing files that changed from the base of the PR and between 14ea70c and bad417a.

📒 Files selected for processing (3)
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When building HttpResponse from Django-template-rendered HTML in views, use format_html() to compose the envelope and mark_safe() on the inner HTML to clear CodeQL py/reflected-xss false positives. Example: format_html('<div id="target" hx-swap-oob="innerHTML">{}</div>', mark_safe(modal_html))

Files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
🧠 Learnings (17)
📚 Learning: 2026-03-07T22:46:57.537Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 21
File: netbox_librenms_plugin/tests/test_librenms_id.py:184-184
Timestamp: 2026-03-07T22:46:57.537Z
Learning: Do not flag or request style-only changes (such as removing redundant imports or cosmetic cleanup) in Python code reviews. Focus on issues that affect correctness, functionality, or security. This guideline applies to Python files across the repository, including tests (e.g., netbox_librenms_plugin/tests/test_librenms_id.py).

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-03-08T13:09:49.031Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/forms.py:75-79
Timestamp: 2026-03-08T13:09:49.031Z
Learning: In multi-server caching within the codebase, ensure poller group choices and similar cache discriminators use api.server_key as the cache key component (e.g., cache_key = f"librenms_poller_group_choices_{api.server_key}") rather than api.librenms_url. This aligns with the fixed approach seen in commit bf37f07 and with other modules. Apply this pattern consistently to Python files under netbox_librenms_plugin (and similar multi-server cache keys) to maintain correct cross-server caching behavior.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-05-05T09:51:15.707Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 58
File: netbox_librenms_plugin/views/base/modules_view.py:311-313
Timestamp: 2026-05-05T09:51:15.707Z
Learning: In this repository, if you see `timezone.timedelta` used from `django.utils.timezone`, do not request a diff to replace it with `datetime.timedelta` solely on style grounds. This usage is functionally correct because Django exposes `timedelta` via `django.utils.timezone`; treat this as intentional and avoid churn unless there is evidence of changed/incorrect behavior (e.g., `timezone` is not Django’s module or `timedelta` is unavailable).

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-01T13:35:47.228Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/views/sync/migrate.py:177-181
Timestamp: 2026-06-01T13:35:47.228Z
Learning: When reviewing this plugin’s permission checks, note that `check_object_permissions` / `NetBoxObjectPermissionMixin` enforce only **model-level** permissions: they call `request.user.has_perm(perm)` without any object/row instance, and the plugin does not currently implement per-object (row-level) permission scoping. Therefore, do **not** flag “missing winner-side/per-object object-permission checks” in sync/migrate views (or elsewhere in the plugin) as a defect; per-object permission scoping is an intentional plugin-wide design gap to be addressed in a dedicated future PR.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-07-01T16:41:50.451Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 113
File: netbox_librenms_plugin/tables/cables.py:62-76
Timestamp: 2026-07-01T16:41:50.451Z
Learning: When rendering Bootstrap/Tabler badges in this repo (netbox-librenms-plugin), always pair a solid background utility `bg-*` with an explicit text utility that provides appropriate contrast (e.g., `bg-danger` + `text-white`, `bg-warning` + `text-dark`, `bg-purple` + `text-white`). Follow the existing badge contrast convention enforced by `netbox_librenms_plugin/tests/test_badge_contrast.py`. Do not suggest Tabler’s semantic `text-*-fg` token classes (e.g., `text-purple-fg`), since they are not used in this codebase and are not covered by the contrast test.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-07-30T02:40:53.531Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 123
File: netbox_librenms_plugin/views/base/modules_view.py:0-0
Timestamp: 2026-07-30T02:40:53.531Z
Learning: In the NetBox LibreNMS plugin, normalize all LibreNMS serial values via `netbox_librenms_plugin.utils.normalize_serial()` before storing/comparing them. Treat only `None` as “absent”; for any other value (including numeric/falsey values like `0` or `False`), convert using `str(value).strip()` inside the normalizer. For identity/conflict checks against existing NetBox `Device` rows, compare using trimmed serial matching (i.e., compare normalized/stripped serial strings) so legacy device serials with surrounding whitespace still match normalized incoming serials.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-08-03T19:16:41.198Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 87
File: netbox_librenms_plugin/views/base/ip_addresses_view.py:502-502
Timestamp: 2026-08-03T19:16:41.198Z
Learning: For migration-marker handling in the NetBox LibreNMS plugin, use `get_migrated_to_marker()` and `mark_librenms_migrated()` as the centralized read/write chokepoints rather than duplicating marker logic. Normalize blank or `None` `server_key` values to `"default"`. Derive migration UI context with `build_migrated_context()` and pass it through `render_sync_partial()` to the interface, IP, cable, module, and VLAN sync partials.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-03-27T02:04:22.276Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/tests/test_coverage_api.py:893-939
Timestamp: 2026-03-27T02:04:22.276Z
Learning: For unit tests in this repo (e.g., coverage API tests), when testing a happy-path call like `add_device()`, assert both the success flag and the expected success message (e.g., `assert ok is True` and `assert msg == "Device added successfully."`). This ensures the test fails if `add_device()` returns `(False, ...)`. If a related assertion is explicitly tracked as a known deferred follow-up for a prior PR, do not treat the missing `ok is True` assertion as a new review finding in subsequent reviews.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-02T11:11:56.131Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/tests/test_coverage_actions.py:4773-4776
Timestamp: 2026-06-02T11:11:56.131Z
Learning: When application code performs a function-local import inside a method body (e.g., `from utilities.permissions import get_permission_for_model`), unit tests should patch the original source attribute (`utilities.permissions.get_permission_for_model`). Do not patch the consumer module’s name (e.g., `netbox_librenms_plugin.views.imports.actions.get_permission_for_model`) unless the function is imported at module scope and exposed as a module attribute—local imports re-resolve the attribute at call time.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-02T20:43:51.604Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/tests/test_coverage_device_operations.py:2466-2478
Timestamp: 2026-06-02T20:43:51.604Z
Learning: When reviewing tests under netbox_librenms_plugin/tests, don’t treat intentional stubs/mocks of lower-layer helper functions as a “coverage hole” if the test’s goal is to isolate and verify only the validate-layer (or another single unit of behavior). If the stubbed helper’s actual logic is exercised in dedicated tests at the helper/service layer (e.g., test_*_helper* / test_librenms_id.py), it’s acceptable for the validate-layer test to control helper outputs (via side_effect/return values) and assert the validate-layer mapping/selection logic only. Flag only when the stub hides untested logic that should belong to the unit under test (i.e., the test asserts behavior from the helper without actually verifying the unit’s own responsibility).

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-15T18:49:04.201Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 104
File: netbox_librenms_plugin/tests/test_coverage_actions.py:1866-1870
Timestamp: 2026-06-15T18:49:04.201Z
Learning: When reviewing tests related to the LibreNMS device ID migration flow (e.g., `migrate_librenms_id` / `migrate_legacy_librenms_id`), do not require `validation["librenms_id_needs_migration"] == True` solely for test setup. That flag is only used for UI visibility in `device_status.py` / `device_validation_details.html`; the backend migration action is gated by the instance’s legacy raw value (`custom_field_data["librenms_id"]` matching the active LibreNMS device id) plus the `serial_confirmed` or `force` condition. If the test already pins/executes migration by asserting the migration function was called with the locked instance (or otherwise directly forces execution), it should be acceptable even when `librenms_id_needs_migration` is not set to True.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-17T07:31:54.849Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 104
File: netbox_librenms_plugin/tests/test_librenms_api.py:2258-2258
Timestamp: 2026-06-17T07:31:54.849Z
Learning: When reviewing Python test code in netbox_librenms_plugin/tests, treat “develop-owned” scaffold lines as off-limits for in-PR rewrites. A line is “develop-owned” if `git blame` for that line attributes it to a commit that is an ancestor of `origin/develop` (i.e., the commit is contained in `origin/develop`). For such lines, reviewers should acknowledge the findings as valid but defer the change by creating/using a follow-up issue targeting the `develop` branch (e.g., `#112`), rather than requesting modifications in the current feature/PR stack.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-19T14:03:09.440Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 113
File: netbox_librenms_plugin/tests/test_coverage_base_views2.py:555-567
Timestamp: 2026-06-19T14:03:09.440Z
Learning: In tests under netbox_librenms_plugin/tests, don’t rely on “pure” MagicMock setups that stub chained calls like `interfaces.filter.return_value.first.return_value` when the code under test is supposed to distinguish between (1) a librenms_id custom-field lookup and (2) a name-based fallback lookup. If the mock returns the same interface regardless of filter arguments, the test cannot detect which lookup path matched (renaming variables like `remote_port` doesn’t fix this). Use a real-DB hardening test instead: create/seed a `remote_port` value that is deliberately different from the actual interface name so only the librenms_id CF lookup can produce a match. If an existing MagicMock-masked test file is develop-inherited (identical on origin/develop), don’t modify it in feature PRs; add a new real-DB hardening test file (e.g., `test_enrich_remote_port_realdb.py`) on the develop-targeted branch.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-06-25T07:07:59.192Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 114
File: netbox_librenms_plugin/tests/test_oob_sync_review_fixes.py:0-0
Timestamp: 2026-06-25T07:07:59.192Z
Learning: When writing/adjusting tests (and any review/test helper code) that interact with NetBox’s custom User model, do not assume the user model has an `is_staff` field. If you need to check or set user privileges/eligibility, use fields that are known to exist on the NetBox User model (e.g., `is_superuser` and `is_active`) instead. Avoid setting `is_staff` (it can raise Django `FieldError` if that field doesn’t exist on the custom model).

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-07-02T21:46:46.384Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 116
File: netbox_librenms_plugin/tests/test_coverage_bulk_import.py:132-140
Timestamp: 2026-07-02T21:46:46.384Z
Learning: In netbox_librenms_plugin/tests, keep the `_stub_norm_preload` autouse fixture (which patches `netbox_librenms_plugin.import_utils.bulk_import.preload_normalization_rules` to return `{}`) intentionally duplicated per mock-based test class/module. Do NOT hoist it into a global `conftest.py` autouse fixture, because that would apply the patch repo-wide and mask the real `preload_normalization_rules` behavior in real-DB tests that are meant to exercise it (see issue `#90`). If you need this patch, scope it to the specific mock-based tests that should stub normalization rules.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-04-01T15:55:42.180Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 50
File: netbox_librenms_plugin/tests/test_coverage_actions.py:88-171
Timestamp: 2026-04-01T15:55:42.180Z
Learning: When unit/integration testing actions that indirectly use a function imported at module import time, patch the function where it is *used* (the consumer’s import path), e.g. `netbox_librenms_plugin.views.imports.actions.resolve_naming_preferences`, rather than its original definition. For tests that target the function itself directly, patch the original dependency/definition (e.g. `netbox_librenms_plugin.utils.get_user_pref` or patch `resolve_naming_preferences` at `netbox_librenms_plugin.utils`) so the function under test sees the mocked behavior.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
📚 Learning: 2026-05-05T09:46:17.700Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 58
File: netbox_librenms_plugin/tests/test_vm_operations.py:46-46
Timestamp: 2026-05-05T09:46:17.700Z
Learning: When the code under test performs *lazy imports* inside function bodies (i.e., the imported symbol is not bound at the module scope), mock/patch the *source module path that the function imports from*, not the consumer module path. The correct patch target is where the imported name is resolved at runtime (e.g., `virtualization.models.VirtualMachine`), because patching `netbox_librenms_plugin.import_utils.vm_operations.VirtualMachine` can fail with `AttributeError` since `VirtualMachine` is never a `vm_operations` module attribute.

Applied to files:

  • netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_validation_template_server_key.py
🪛 ast-grep (0.45.0)
netbox_librenms_plugin/tests/test_validation_template_server_key.py

[warning] 27-27: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.search(name_pattern, tag, re.IGNORECASE)
Note: [CWE-1333] Inefficient Regular Expression Complexity.

(redos-non-literal-regex-python)

🔇 Additional comments (3)
netbox_librenms_plugin/tests/test_vlan_sync_concurrency.py (1)

16-96: LGTM!

netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py (1)

757-762: LGTM!

netbox_librenms_plugin/tests/test_validation_template_server_key.py (1)

20-22: LGTM!

Also applies to: 25-29, 45-46, 63-84


📝 Walkthrough

Walkthrough

This PR adds bulk collision pre-checking, permission-scoped object access, cache and linkage validation, shared value normalization, and consolidated mapping and rule navigation.

Changes

Import safety and execution

Layer / File(s) Summary
Bulk collision detection and classification
netbox_librenms_plugin/import_utils/*, netbox_librenms_plugin/views/imports/actions.py, netbox_librenms_plugin/jobs.py, netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/*
Bulk imports detect shared Device and VirtualMachine targets. Collisions block the batch. Unresolved rows are skipped and reported.
Permission-aware synchronization
netbox_librenms_plugin/views/mixins.py, netbox_librenms_plugin/views/sync/*, netbox_librenms_plugin/views/object_sync/devices.py
Object lookups use permission-restricted querysets. Synchronization checks owning-object permissions before processing.
State validation and normalization
netbox_librenms_plugin/utils.py, netbox_librenms_plugin/import_validation_helpers.py, netbox_librenms_plugin/views/base/modules_view.py, netbox_librenms_plugin/views/sync/migrate.py
Cache identity, positive IDs, LibreNMS text, migration marker scopes, and locked relations use shared validation and normalization paths.

Consolidated navigation

Layer / File(s) Summary
Mapping and rule navigation
netbox_librenms_plugin/navigation.py, netbox_librenms_plugin/templates/netbox_librenms_plugin/*_list.html, netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/*tabs.html, docs/usage_tips/*
Sidebar entries target grouped Mappings and Rules & Patterns pages. Shared tabs link sibling lists and mark the active page. Documentation uses the new paths.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit checks each imported row,
Collisions stop the batch in place.
Tabs guide mappings where they go,
Scoped lookups guard each space.
“Hop,” says the rabbit, “validate the key!” 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary navigation change.
Description check ✅ Passed The description follows the template and clearly covers the change, motivation, scope, testing, risk, and compatibility.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/nav-consolidation
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/nav-consolidation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
netbox_librenms_plugin/import_utils/bulk_import.py (1)

897-936: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Matched VM's cluster is never populated (asymmetry with the device_role branch).

For a device match, apply_role_to_validation(validation, new_device.role, ...) populates device_role with the matched device's actual role for display. For a VM match (actual_is_vm=True), there's no equivalent call to set validation["cluster"] to the matched VM's actual cluster — the field is left at whatever stale/default value it had before. can_import/is_ready are still correctly forced False afterward, so this doesn't affect import gating, but it leaves the "existing match" UI showing an unselected/empty cluster for a VM that in fact already has one, unlike the device-role display.

🐛 Proposed fix for VM cluster symmetry
             if not actual_is_vm and hasattr(new_device, "role") and new_device.role:
                 apply_role_to_validation(validation, new_device.role, is_vm=False)
             elif not actual_is_vm:
                 validation["device_role"] = {
                     "found": False,
                     "role": None,
                     "available_roles": validation.get("device_role", {}).get("available_roles", []),
                 }
+            elif actual_is_vm and hasattr(new_device, "cluster") and new_device.cluster:
+                apply_cluster_to_validation(validation, new_device.cluster)
+            elif actual_is_vm:
+                validation["cluster"] = {
+                    "found": False,
+                    "cluster": None,
+                    "available_clusters": validation.get("cluster", {}).get("available_clusters", []),
+                }
             recalculate_validation_status(validation, is_vm=actual_is_vm)

apply_cluster_to_validation is already defined in import_validation_helpers.py; ensure it's imported here alongside apply_role_to_validation.

Based on learnings: keep available_roles and available_clusters populated for both new and existing-device cases so UI dropdowns function correctly on update views — the same symmetry principle applies to reflecting the matched object's actual selection, not just the option list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@netbox_librenms_plugin/import_utils/bulk_import.py` around lines 897 - 936,
The existing-match handling in bulk_import.py is asymmetric: the device path
populates the matched role for display, but the VM path never sets the matched
cluster, leaving stale or empty UI state. Update the existing-device branch
around _refresh_librenms_linkage and recalculate_validation_status to call
apply_cluster_to_validation for actual VM matches, and ensure it is imported
alongside apply_role_to_validation so validation["cluster"] reflects the
resolved VM’s real cluster while keeping available_clusters/available_roles
populated.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/usage_tips/interface_mappings.md`:
- Line 32: The screenshot markdown in the interface mappings doc is missing alt
text, which triggers the MD045 warning. Update the image syntax to include a
meaningful description of the screenshot content instead of an empty alt field,
keeping the existing image reference and width option intact.

In `@netbox_librenms_plugin/import_utils/bulk_import.py`:
- Around line 492-521: The handling of merge_candidates is inconsistent:
_clear_existing_match_derived_fields removes the key entirely, while
apply_oob_detection_result and validate_device_for_import keep it present with a
None value. Update _clear_existing_match_derived_fields to preserve the existing
contract by setting validation["merge_candidates"] to None instead of popping
it, so downstream code using direct key access does not hit a KeyError.

In `@netbox_librenms_plugin/tables/interfaces.py`:
- Around line 303-313: The OOB badge check in render_name is duplicated in
another table renderer, so centralize it in a shared helper. Add a small utility
like oob_badge_html(record) near render_vc_member_options in utils.py, move the
record.get("_source") == "oob" logic and OOB_BADGE_HTML markup there, then
update render_name (and the matching code in tables/modules.py) to call the
helper instead of building the badge inline.

In
`@netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync_content.html`:
- Around line 14-25: The migrated interface-sync branch in
_interface_sync_content.html currently renders only the CSRF token, but
handleInterfaceChange() expects a server_key input as well. Update the
migrated-mode block so it also emits a hidden server_key field alongside
csrfmiddlewaretoken, ensuring the JS-driven verify-interface flow stays scoped
to the correct server on non-default deployments.

In
`@netbox_librenms_plugin/templates/netbox_librenms_plugin/_module_sync_content.html`:
- Around line 19-40: The `_module_sync_content.html` template still duplicates
the same `module_sync.server_key` hidden input logic in both the install form
branch and the migrated branch. Replace both inline conditionals with the shared
`inc/_hidden_server_key.html` partial, matching the reuse already done in
`_module_sync.html` and `_vlan_sync.html`, so `module_sync.server_key` is
rendered through one central template path.

In
`@netbox_librenms_plugin/templates/netbox_librenms_plugin/_vlan_sync_content.html`:
- Line 35: The VLAN sync template is duplicating the hidden server key input
pattern instead of reusing the shared partial. Update _vlan_sync_content.html to
render inc/_hidden_server_key.html the same way _module_sync_content.html does,
using the vlan_sync server key context so the hidden-input contract stays
single-sourced and consistent.

In
`@netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html`:
- Around line 748-761: The “Add as OOB” form markup is duplicated in the
`device_validation_details.html` template across the `serial` and `primary_ip`
match branches, which risks drift. Extract the shared form block used in
`device_add_as_oob`, including the CSRF token, `existing_device_id`,
`_hidden_server_key.html`, `_oob_interface_select.html`, and submit button, into
a reusable partial and include it from both branches to keep behavior
consistent.

In `@netbox_librenms_plugin/tests/test_coverage_bulk_import.py`:
- Around line 806-818: The `_device_validation()` test fixture is missing the
always-present "cluster" entry that real `validate_device_for_import()` results
include, which can lead to a future KeyError in `recalculate_validation_status`
when cross-model VM/device matching accesses `validation["cluster"]["found"]`.
Update the baseline dict in `_device_validation()` to include a realistic
"cluster" key alongside the existing `device_role` fields so any tests using
this helper match the actual validation shape.

---

Outside diff comments:
In `@netbox_librenms_plugin/import_utils/bulk_import.py`:
- Around line 897-936: The existing-match handling in bulk_import.py is
asymmetric: the device path populates the matched role for display, but the VM
path never sets the matched cluster, leaving stale or empty UI state. Update the
existing-device branch around _refresh_librenms_linkage and
recalculate_validation_status to call apply_cluster_to_validation for actual VM
matches, and ensure it is imported alongside apply_role_to_validation so
validation["cluster"] reflects the resolved VM’s real cluster while keeping
available_clusters/available_roles populated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: b3d8d7f6-9d6f-45b4-bd7f-410699606f63

📥 Commits

Reviewing files that changed from the base of the PR and between e6def35 and 537e76b.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (127)
  • .gitignore
  • docs/SUMMARY.md
  • docs/feature_list.md
  • docs/librenms_import/validation.md
  • docs/usage_tips/custom_field.md
  • docs/usage_tips/interface_mappings.md
  • docs/usage_tips/mapping_rules.md
  • docs/usage_tips/oob_management.md
  • mkdocs.yml
  • netbox_librenms_plugin/constants.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/librenms_api.py
  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_import.js
  • netbox_librenms_plugin/tables/cables.py
  • netbox_librenms_plugin/tables/device_status.py
  • netbox_librenms_plugin/tables/interfaces.py
  • netbox_librenms_plugin/tables/ipaddresses.py
  • netbox_librenms_plugin/tables/modules.py
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_cable_sync.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_cable_sync_content.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync_content.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_ipaddress_sync.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_ipaddress_sync_content.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_module_sync.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_module_sync_content.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_vlan_sync.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/_vlan_sync_content.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/_dt_mapping_form.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/_existing_librenms_link_status.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/_oob_interface_select.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/_platform_mapping_form.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/create_platform_modal.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_import_row.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_hidden_server_key.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_migrate_move_button.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/tests/_html_helpers.py
  • netbox_librenms_plugin/tests/conftest.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/tests/test_badge_contrast.py
  • netbox_librenms_plugin/tests/test_cable_sync_content_template.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_coverage_actions.py
  • netbox_librenms_plugin/tests/test_coverage_api.py
  • netbox_librenms_plugin/tests/test_coverage_base_views.py
  • netbox_librenms_plugin/tests/test_coverage_base_views2.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_coverage_device_fields.py
  • netbox_librenms_plugin/tests/test_coverage_device_operations.py
  • netbox_librenms_plugin/tests/test_coverage_devices.py
  • netbox_librenms_plugin/tests/test_coverage_list.py
  • netbox_librenms_plugin/tests/test_coverage_mixins.py
  • netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py
  • netbox_librenms_plugin/tests/test_coverage_sync_view.py
  • netbox_librenms_plugin/tests/test_coverage_sync_views.py
  • netbox_librenms_plugin/tests/test_coverage_sync_views2.py
  • netbox_librenms_plugin/tests/test_coverage_tables.py
  • netbox_librenms_plugin/tests/test_coverage_utils.py
  • netbox_librenms_plugin/tests/test_coverage_virtual_chassis.py
  • netbox_librenms_plugin/tests/test_device_fields_server_scoping.py
  • netbox_librenms_plugin/tests/test_device_validation_details_template.py
  • netbox_librenms_plugin/tests/test_import_utils.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_interface_sync_content_template.py
  • netbox_librenms_plugin/tests/test_ip_verify.py
  • netbox_librenms_plugin/tests/test_ipaddress_sync_content_template.py
  • netbox_librenms_plugin/tests/test_librenms_api.py
  • netbox_librenms_plugin/tests/test_librenms_api_server_config.py
  • netbox_librenms_plugin/tests/test_librenms_id.py
  • netbox_librenms_plugin/tests/test_migrate_views.py
  • netbox_librenms_plugin/tests/test_mixins.py
  • netbox_librenms_plugin/tests/test_module_sync_content_template.py
  • netbox_librenms_plugin/tests/test_modules_view.py
  • netbox_librenms_plugin/tests/test_multiserver_get_cache_scoping.py
  • netbox_librenms_plugin/tests/test_permissions.py
  • netbox_librenms_plugin/tests/test_reviewer_fixes.py
  • netbox_librenms_plugin/tests/test_rules_patterns_navigation.py
  • netbox_librenms_plugin/tests/test_server_key_in_redirects.py
  • netbox_librenms_plugin/tests/test_sync_devices.py
  • netbox_librenms_plugin/tests/test_sync_modules.py
  • netbox_librenms_plugin/tests/test_sync_page_server_key_forms.py
  • netbox_librenms_plugin/tests/test_sync_view_mismatch.py
  • netbox_librenms_plugin/tests/test_sync_view_unresolved_vc.py
  • netbox_librenms_plugin/tests/test_tables_modules.py
  • netbox_librenms_plugin/tests/test_utils.py
  • netbox_librenms_plugin/tests/test_utils_shared_helpers.py
  • netbox_librenms_plugin/tests/test_vlan_sync.py
  • netbox_librenms_plugin/urls.py
  • netbox_librenms_plugin/utils.py
  • netbox_librenms_plugin/views/__init__.py
  • netbox_librenms_plugin/views/base/cables_view.py
  • netbox_librenms_plugin/views/base/interfaces_view.py
  • netbox_librenms_plugin/views/base/ip_addresses_view.py
  • netbox_librenms_plugin/views/base/librenms_sync_view.py
  • netbox_librenms_plugin/views/base/modules_view.py
  • netbox_librenms_plugin/views/base/vlan_table_view.py
  • netbox_librenms_plugin/views/imports/actions.py
  • netbox_librenms_plugin/views/imports/list.py
  • netbox_librenms_plugin/views/mixins.py
  • netbox_librenms_plugin/views/object_sync/devices.py
  • netbox_librenms_plugin/views/sync/cables.py
  • netbox_librenms_plugin/views/sync/device_fields.py
  • netbox_librenms_plugin/views/sync/devices.py
  • netbox_librenms_plugin/views/sync/interfaces.py
  • netbox_librenms_plugin/views/sync/ip_addresses.py
  • netbox_librenms_plugin/views/sync/migrate.py
  • netbox_librenms_plugin/views/sync/modules.py

Comment thread docs/usage_tips/interface_mappings.md Outdated
Comment thread netbox_librenms_plugin/import_utils/bulk_import.py Outdated
Comment thread netbox_librenms_plugin/tables/interfaces.py
Comment thread netbox_librenms_plugin/templates/netbox_librenms_plugin/_module_sync_content.html Outdated
Comment thread netbox_librenms_plugin/templates/netbox_librenms_plugin/_vlan_sync_content.html Outdated
Comment thread netbox_librenms_plugin/tests/test_coverage_bulk_import.py
@marcinpsk
marcinpsk force-pushed the feat/nav-consolidation branch from 537e76b to 98de27d Compare July 7, 2026 11:29
Comment thread netbox_librenms_plugin/import_utils/bulk_import.py Outdated
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@marcinpsk
marcinpsk force-pushed the feat/nav-consolidation branch from 98de27d to b25eb79 Compare July 7, 2026 13:52
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@marcinpsk
marcinpsk force-pushed the feat/nav-consolidation branch from b25eb79 to bb48f0e Compare July 7, 2026 22:15
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
netbox_librenms_plugin/import_utils/device_operations.py (1)

466-1382: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Very large, deeply-nested function.

validate_device_for_import() now spans ~900 lines with many interleaved terminal-state branches (ambiguous id, duplicate hostname/serial, duplicate IP, merge-candidate detection). The logic itself is correct and thoroughly commented/tested, but the sheer size makes it hard to reason about future changes safely. Consider extracting self-contained stages (e.g., the "Stage 1 duplicate-peer" check and "Stage 2 merge-candidate" block at lines 873-1044) into named helper functions once the current stack of dependent PRs settles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@netbox_librenms_plugin/import_utils/device_operations.py` around lines 466 -
1382, validate_device_for_import() has grown into a very large, deeply nested
validation flow, making future changes risky. Split the self-contained
terminal-state sections into named helpers, especially the Stage 1
duplicate-peer guard and Stage 2 merge-candidate logic inside
validate_device_for_import, so the main function reads as a high-level
orchestration over smaller units with clear responsibilities.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@netbox_librenms_plugin/views/imports/actions.py`:
- Around line 971-983: The collision check in detect_collisions_for_device_ids
should reuse and persist the LibreNMS rows it fetches instead of only reading
from libre_devices_cache. Update the helper so any cache misses are written back
into the shared cache object (or otherwise returned to the caller) before the
import flow continues, and ensure the actions.py import path keeps using that
populated cache for the later device-import step.
- Around line 3311-3324: The merge flow in the candidate/device selection logic
resolves `winner_sync` and `donor_sync` too early in relation to the transaction
lock. Move the `get_librenms_sync_device()` lookups into the atomic section
after the candidate devices are locked, or extend the lock to cover the
VC-related rows used by that lookup, so `winner_sync`/`donor_sync` are computed
from current state before the merge write.

---

Outside diff comments:
In `@netbox_librenms_plugin/import_utils/device_operations.py`:
- Around line 466-1382: validate_device_for_import() has grown into a very
large, deeply nested validation flow, making future changes risky. Split the
self-contained terminal-state sections into named helpers, especially the Stage
1 duplicate-peer guard and Stage 2 merge-candidate logic inside
validate_device_for_import, so the main function reads as a high-level
orchestration over smaller units with clear responsibilities.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 19a8e86a-377f-422a-9e1b-b8203ebc6e8e

📥 Commits

Reviewing files that changed from the base of the PR and between b25eb79 and bb48f0e.

📒 Files selected for processing (33)
  • docs/usage_tips/interface_mappings.md
  • docs/usage_tips/mapping_rules.md
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_coverage_actions.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_coverage_device_operations.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_migrate_views.py
  • netbox_librenms_plugin/tests/test_rules_patterns_navigation.py
  • netbox_librenms_plugin/tests/test_utils.py
  • netbox_librenms_plugin/utils.py
  • netbox_librenms_plugin/views/imports/actions.py
  • netbox_librenms_plugin/views/sync/modules.py

Comment thread netbox_librenms_plugin/views/imports/actions.py Outdated
Comment thread netbox_librenms_plugin/views/imports/actions.py Outdated
@marcinpsk
marcinpsk force-pushed the feat/nav-consolidation branch from bb48f0e to 5c22a7a Compare July 8, 2026 15:56
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@marcinpsk
marcinpsk force-pushed the feat/nav-consolidation branch from 5c22a7a to f926c98 Compare July 9, 2026 04:47
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
netbox_librenms_plugin/views/imports/actions.py (1)

1047-1060: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not queue Django messages on the HTMX success path.

messages.info(...) runs even when is_htmx is true, but this view returns an HX redirect instead of rendering the message queue. That leaks the flash message into the next full-page request and contradicts the surrounding comment.

♻️ Proposed fix
-                messages.info(
-                    request,
-                    format_html(
-                        "Import job started for {} device{}. "
-                        'You can monitor progress in the <a href="{}">Jobs interface</a>.',
-                        total_import_count,
-                        "s" if total_import_count != 1 else "",
-                        job_url,
-                    ),
-                )
+                if not is_htmx:
+                    messages.info(
+                        request,
+                        format_html(
+                            "Import job started for {} device{}. "
+                            'You can monitor progress in the <a href="{}">Jobs interface</a>.',
+                            total_import_count,
+                            "s" if total_import_count != 1 else "",
+                            job_url,
+                        ),
+                    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@netbox_librenms_plugin/views/imports/actions.py` around lines 1047 - 1060,
The success path in the import action is always calling messages.info even for
HTMX requests, which can leak a flash message into the next full-page load.
Update the logic around the job_url and messages.info block in actions.py so
that Django messages are only queued on the non-HTMX path, while the is_htmx
branch returns the HX redirect without adding a message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@netbox_librenms_plugin/views/imports/actions.py`:
- Around line 1047-1060: The success path in the import action is always calling
messages.info even for HTMX requests, which can leak a flash message into the
next full-page load. Update the logic around the job_url and messages.info block
in actions.py so that Django messages are only queued on the non-HTMX path,
while the is_htmx branch returns the HX redirect without adding a message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 840c3a67-69ab-4086-9be6-a19922abedc4

📥 Commits

Reviewing files that changed from the base of the PR and between bb48f0e and f926c98.

📒 Files selected for processing (33)
  • docs/usage_tips/interface_mappings.md
  • docs/usage_tips/mapping_rules.md
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_coverage_actions.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_coverage_device_operations.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_migrate_views.py
  • netbox_librenms_plugin/tests/test_rules_patterns_navigation.py
  • netbox_librenms_plugin/tests/test_utils.py
  • netbox_librenms_plugin/utils.py
  • netbox_librenms_plugin/views/imports/actions.py
  • netbox_librenms_plugin/views/sync/modules.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
netbox_librenms_plugin/templates/**/*.html

📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

netbox_librenms_plugin/templates/**/*.html: HTMX 2.x is the primary async layer. Table row updates should return <tr hx-swap-oob="true">.
Avoid outerHTML swaps in HTMX; use OOB or targeted innerHTML swaps to keep table layout intact.
Do not reintroduce data-bs-toggle or duplicate modal IDs in modal implementation.
Keep <select class="device-role-select"> markup stable to preserve JavaScript hook-up for TomSelect decorators.
Do not re-add table-responsive wrappers as their removal was deliberate to prevent dropdown clipping.
Templates live in templates/netbox_librenms_plugin/; reuse and includes go under inc/ subdirectory.

Files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
netbox_librenms_plugin/**/*.{html,js}

📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

All HTMX requests and fetch() calls must include a CSRF token. Prefer extracting from hidden form input via document.querySelector('[name=csrfmiddlewaretoken]').value rather than cookie-based approach.

Files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
netbox_librenms_plugin/**/*.{html,css}

📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

Styling assumes Tabler defaults for the netbox_librenms_plugin frontend.

Files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/*.html

📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

HTMX fragments live in templates/netbox_librenms_plugin/htmx/ including: device_import_row.html, device_validation_details.html, device_vc_details.html, bulk_import_confirm.html.

Files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When building HttpResponse from Django-template-rendered HTML in views, use format_html() to compose the envelope and mark_safe() on the inner HTML to clear CodeQL py/reflected-xss false positives. Example: format_html('<div id="target" hx-swap-oob="innerHTML">{}</div>', mark_safe(modal_html))

Files:

  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/views/imports/actions.py
**/import_utils/__init__.py

📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

import_utils/ is a package; the __init__.py must re-export key functions so callers can use from import_utils import ... for functions from filters.py, device_operations.py, vm_operations.py, cache.py, permissions.py, and virtual_chassis.py

Files:

  • netbox_librenms_plugin/import_utils/__init__.py
**/jobs.py

📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

**/jobs.py: Background jobs must use NetBox's JobRunner base class (netbox.jobs.JobRunner) for long-running operations like device filtering with VC detection
Use Job UUID (job.job_id) for RQ API endpoints (/api/core/background-tasks/{uuid}/) and Job PK (job.pk) for database endpoints and result loading
RQ status values are: queued, started, finished, stopped, failed (NOT completed). Database Job status values are: pending, scheduled, running, completed, failed, errored (NO cancelled status exists)
Check rq_job.is_stopped or rq_job.is_failed flags in Redis for cancellation detection, not database status
FilterDevicesJob background job — filters devices with VC detection. job.data keys: device_ids, total_processed, filters, server_key, vc_detection_enabled, cache_timeout, cached_at, completed. Devices cached individually via shared cache keys from get_validated_device_cache_key()
ImportDevicesJob background job — imports devices/VMs. Calls bulk_import_devices_shared() for devices and bulk_import_vms() for VMs. job.data keys: imported_device_pks, imported_vm_pks, imported_libre_device_ids, imported_libre_vm_ids, server_key, total, success_count, failed_count, skipped_count, virtual_chassis_created, errors, completed
Both synchronous and background modes must use get_validated_device_cache_key() from import_utils.py to generate cache keys, ensuring _load_job_results() in the list view can retrieve devices regardless of which mode produced them. Never hardcode cache key formats; always use the helper functions
Use standalone permission helper functions from import_utils.py for permission checks inside job code: check_user_permissions(user, permissions)(bool, missing_list) and require_permissions(user, permissions, action_description) — raises PermissionDenied. Background jobs run outside view context and cannot use view mixins

Files:

  • netbox_librenms_plugin/jobs.py
**/import_validation_helpers.py

📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

**/import_validation_helpers.py: import_validation_helpers.py must provide validation state mutation functions: apply_role_to_validation(), apply_cluster_to_validation(), apply_rack_to_validation() for updating validation state when user selects a role/cluster/rack, and remove_validation_issue(), recalculate_validation_status() for maintaining issue list and overall status
import_validation_helpers.py must provide helper functions: fetch_model_by_id() and extract_device_selections() for reading form data

Files:

  • netbox_librenms_plugin/import_validation_helpers.py
**/import_utils/device_operations.py

📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

device_operations.py must export: validate_device_for_import(device, ...) and bulk_import_devices_shared(devices, user, ...)

Files:

  • netbox_librenms_plugin/import_utils/device_operations.py
**/views/imports/**

📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

**/views/imports/**: NetBox's /api/core/background-tasks/ endpoint requires superuser (IsSuperuser in BaseRQViewSet). Non-superuser users cannot poll job status (403 Forbidden). Plugin automatically falls back to synchronous mode for non-superusers via should_use_background_job() in list.py and actions.py
Import page filter fields: librenms_location, librenms_type, librenms_os, librenms_hostname, librenms_sysname, librenms_hardware, enable_vc_detection, show_disabled, exclude_existing

Files:

  • netbox_librenms_plugin/views/imports/actions.py
**/views/imports/actions.py

📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

**/views/imports/actions.py: DeviceImportHelperMixin provides get_validated_device_with_selections() and render_device_row() for HTMX row rendering, shared by update views
BulkImportConfirmView (POST) — renders confirmation modal with selected device list via htmx/bulk_import_confirm.html
BulkImportDevicesView (POST) — executes import. Background mode enqueues ImportDevicesJob; sync mode calls bulk_import_devices() + bulk_import_vms() and returns OOB row swaps with HX-Trigger: closeModal
DeviceValidationDetailsView (GET) — renders expandable validation details via htmx/device_validation_details.html
DeviceVCDetailsView (GET) — renders VC member details via htmx/device_vc_details.html
DeviceRoleUpdateView, DeviceClusterUpdateView, DeviceRackUpdateView (POST) — per-device dropdown updates. Apply selection to validation state and return re-rendered row via render_device_row()

Files:

  • netbox_librenms_plugin/views/imports/actions.py
🧠 Learnings (31)
📚 Learning: 2026-03-13T11:16:36.294Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html:136-137
Timestamp: 2026-03-13T11:16:36.294Z
Learning: In Django templates under netbox_librenms_plugin/templates/**/*.html, do not suggest adding explicit parentheses to {% if %} expressions for readability. The project favors compact expressions using implicit operator precedence (and binds tighter than or). Treat parentheses as cosmetic and avoid guidance to insert them for style reasons.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-05-01T08:25:06.260Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 50
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html:245-246
Timestamp: 2026-05-01T08:25:06.260Z
Learning: In netbox_librenms_plugin template HTML/HTMX code, only require an X-CSRFToken header for state-changing requests made via fetch() or HTMX (POST, PUT, PATCH, DELETE). Do not require X-CSRFToken on read-only fetch() GET calls (e.g., autocomplete/lookup endpoints like dcim-api:devicetype-list); Django/DRF exempt GET requests from CSRF validation. Therefore, code reviews should not flag missing CSRF headers on GET fetch() calls used for lookups/autocomplete.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-06-14T22:58:16.581Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 87
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html:196-196
Timestamp: 2026-06-14T22:58:16.581Z
Learning: In Django template files under netbox_librenms_plugin/templates/**/*.html, do NOT flag template expressions like accessing a chained attribute on a possibly-None variable (e.g., `librenms_sync_device.pk` when `librenms_sync_device` may be None) as a NullPointerError/AttributeError. Django’s template attribute lookup resolves failed lookups to `TEMPLATE_STRING_IF_INVALID` (empty string by default), so comparisons such as `object.pk == librenms_sync_device.pk` will evaluate against `''` and safely result in False rather than raising a template error.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-03-13T20:03:16.435Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/tests/test_coverage_api2.py:319-355
Timestamp: 2026-03-13T20:03:16.435Z
Learning: Do not propose replacing server_info with module_sync.server_key in the templates located under netbox_librenms_plugin/templates/netbox_librenms_plugin (specifically _module_sync.html and inc/_module_sync.html). These templates rely on server_info being present in the parent template context (librenms_sync_base.html) and server_key may be absent on initial load. Treat the correct usage of server_info for populating the value as the intended pattern; only flag issues if server_key is incorrectly used in these templates. This guideline applies to all files under the templates path for this plugin.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-06-01T20:22:57.975Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html:695-700
Timestamp: 2026-06-01T20:22:57.975Z
Learning: Do not recommend adding or propagating the removed `auto_create_ipam` toggle/preference via HTMX (e.g., `hx-include="`#auto-create-ipam-toggle`"`) or by introducing hidden `auto_create_ipam` inputs in out-of-band (OOB) / “promote” POST forms. Since the `auto_create_ipam` feature has been removed from the import page, any review suggestions attempting to wire it into `device_validation_details.html` or other import-flow templates should be ignored.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-06-14T22:58:16.581Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 87
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html:196-196
Timestamp: 2026-06-14T22:58:16.581Z
Learning: In Django templates, the `{% if %}` tag does not support parenthetical grouping. Do not suggest adding parentheses like `{% if (not x) %}` or `{% if (a or b) %}`—these can raise `TemplateSyntaxError` (e.g., “Could not parse the remainder”). Instead, express the logic using Django template operator precedence rules (not binds tighter than and, and binds tighter than or) and refactor (e.g., via separate conditions/`{% if %}` blocks) when precedence alone can’t express the intended grouping.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-07-07T22:23:42.118Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 104
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/portstacklagpattern.html:5-28
Timestamp: 2026-07-07T22:23:42.118Z
Learning: In netbox-librenms-plugin, do not flag NetBox object detail templates for the config/mapping/rule/pattern-style models when they omit including `extras/inc/custom_fields_panel.html` and `extras/inc/tags_panel.html` (even though these models expose `custom_field_data` and/or `TaggableManager`). This omission is a deliberate, plugin-wide UI convention. If changing this behavior (adding custom-fields/tags panels), require a single coordinated proposal spanning the relevant forms and all detail templates together rather than treating individual missing includes as a regression.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
📚 Learning: 2026-05-25T21:48:19.264Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/_platform_mapping_form.html:12-12
Timestamp: 2026-05-25T21:48:19.264Z
Learning: In this plugin’s HTMX form templates, `hx-include` selectors that target toggle preference wrapper `<span>` element IDs (e.g., `#use-sysname-toggle`, `#strip-domain-toggle`, `#auto-create-ipam-toggle`) are intentional. Those wrapper spans contain both the hidden `off` fallback input and the checkbox; HTMX must include the wrapper so the correct value is serialized, including the unchecked/off state. Do not recommend changing `hx-include` to the checkbox IDs with the `-cb` suffix, since it would omit the hidden fallback and break unchecked/off state submission.

Applied to files:

  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
📚 Learning: 2026-03-07T22:46:57.537Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 21
File: netbox_librenms_plugin/tests/test_librenms_id.py:184-184
Timestamp: 2026-03-07T22:46:57.537Z
Learning: Do not flag or request style-only changes (such as removing redundant imports or cosmetic cleanup) in Python code reviews. Focus on issues that affect correctness, functionality, or security. This guideline applies to Python files across the repository, including tests (e.g., netbox_librenms_plugin/tests/test_librenms_id.py).

Applied to files:

  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-03-08T13:09:49.031Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/forms.py:75-79
Timestamp: 2026-03-08T13:09:49.031Z
Learning: In multi-server caching within the codebase, ensure poller group choices and similar cache discriminators use api.server_key as the cache key component (e.g., cache_key = f"librenms_poller_group_choices_{api.server_key}") rather than api.librenms_url. This aligns with the fixed approach seen in commit bf37f07 and with other modules. Apply this pattern consistently to Python files under netbox_librenms_plugin (and similar multi-server cache keys) to maintain correct cross-server caching behavior.

Applied to files:

  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-05-05T09:51:15.707Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 58
File: netbox_librenms_plugin/views/base/modules_view.py:311-313
Timestamp: 2026-05-05T09:51:15.707Z
Learning: In this repository, if you see `timezone.timedelta` used from `django.utils.timezone`, do not request a diff to replace it with `datetime.timedelta` solely on style grounds. This usage is functionally correct because Django exposes `timedelta` via `django.utils.timezone`; treat this as intentional and avoid churn unless there is evidence of changed/incorrect behavior (e.g., `timezone` is not Django’s module or `timedelta` is unavailable).

Applied to files:

  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-06-01T13:35:47.228Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/views/sync/migrate.py:177-181
Timestamp: 2026-06-01T13:35:47.228Z
Learning: When reviewing this plugin’s permission checks, note that `check_object_permissions` / `NetBoxObjectPermissionMixin` enforce only **model-level** permissions: they call `request.user.has_perm(perm)` without any object/row instance, and the plugin does not currently implement per-object (row-level) permission scoping. Therefore, do **not** flag “missing winner-side/per-object object-permission checks” in sync/migrate views (or elsewhere in the plugin) as a defect; per-object permission scoping is an intentional plugin-wide design gap to be addressed in a dedicated future PR.

Applied to files:

  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-07-01T16:41:50.451Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 113
File: netbox_librenms_plugin/tables/cables.py:62-76
Timestamp: 2026-07-01T16:41:50.451Z
Learning: When rendering Bootstrap/Tabler badges in this repo (netbox-librenms-plugin), always pair a solid background utility `bg-*` with an explicit text utility that provides appropriate contrast (e.g., `bg-danger` + `text-white`, `bg-warning` + `text-dark`, `bg-purple` + `text-white`). Follow the existing badge contrast convention enforced by `netbox_librenms_plugin/tests/test_badge_contrast.py`. Do not suggest Tabler’s semantic `text-*-fg` token classes (e.g., `text-purple-fg`), since they are not used in this codebase and are not covered by the contrast test.

Applied to files:

  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-03-08T14:17:28.826Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/import_utils/virtual_chassis.py:73-80
Timestamp: 2026-03-08T14:17:28.826Z
Learning: In Python code, when a lookup returns None (including API failures), implement negative caching by storing an empty result with a configurable TTL (default 5 minutes). Document the TTL and ensure a force_refresh=True bypasses the cache for manual re-fetch actions. Do not treat caching None/empty results on API failure as a bug if this mirrors existing patterns (e.g., get_device_with_server caching None on not-found). Apply this guidance to files within netbox_librenms_plugin/import_utils where similar inventory/API lookups occur.

Applied to files:

  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
📚 Learning: 2026-03-09T20:10:48.502Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 25
File: netbox_librenms_plugin/import_utils/device_operations.py:496-575
Timestamp: 2026-03-09T20:10:48.502Z
Learning: In netbox_librenms_plugin/import_utils/device_operations.py, in validate_device_for_import(), ensure both the cluster-required blocker (VM path) and the device_role-required blocker (device path) are guarded with if not result.get('existing_device') to ensure create-time prerequisites are only appended for new imports, not for link/update flows; keep available_roles and available_clusters populated for both new and existing-device cases so UI dropdowns function correctly on update views.

Applied to files:

  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
📚 Learning: 2026-05-22T19:37:46.167Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/tests/test_import_utils.py:1763-1766
Timestamp: 2026-05-22T19:37:46.167Z
Learning: In `validate_device_for_import` (device operations / serial-diff name-resolution logic), preserve the following result contract so the UI/test expectations remain stable:
- If `serial_action` is determined via a serial match AND the existing device has NO OOB/LibreNMS link, set `serial_action` to `"oob_candidate"` and ensure `promote_to_host` is NOT present in the returned dict.
- Populate `promote_to_host` only when the existing device already has an OOB/LibreNMS link (i.e., a host id is available to inherit from); otherwise omit the key.
- Always include `serial_role_choice_available` in the returned dict, defaulting to `False` (baseline) even when other resolution outcomes do not enable it.

Applied to files:

  • netbox_librenms_plugin/import_utils/collisions.py
  • netbox_librenms_plugin/import_utils/__init__.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
📚 Learning: 2026-03-27T02:04:22.276Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 22
File: netbox_librenms_plugin/tests/test_coverage_api.py:893-939
Timestamp: 2026-03-27T02:04:22.276Z
Learning: For unit tests in this repo (e.g., coverage API tests), when testing a happy-path call like `add_device()`, assert both the success flag and the expected success message (e.g., `assert ok is True` and `assert msg == "Device added successfully."`). This ensures the test fails if `add_device()` returns `(False, ...)`. If a related assertion is explicitly tracked as a known deferred follow-up for a prior PR, do not treat the missing `ok is True` assertion as a new review finding in subsequent reviews.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-06-02T11:11:56.131Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/tests/test_coverage_actions.py:4773-4776
Timestamp: 2026-06-02T11:11:56.131Z
Learning: When application code performs a function-local import inside a method body (e.g., `from utilities.permissions import get_permission_for_model`), unit tests should patch the original source attribute (`utilities.permissions.get_permission_for_model`). Do not patch the consumer module’s name (e.g., `netbox_librenms_plugin.views.imports.actions.get_permission_for_model`) unless the function is imported at module scope and exposed as a module attribute—local imports re-resolve the attribute at call time.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-06-02T20:43:51.604Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/tests/test_coverage_device_operations.py:2466-2478
Timestamp: 2026-06-02T20:43:51.604Z
Learning: When reviewing tests under netbox_librenms_plugin/tests, don’t treat intentional stubs/mocks of lower-layer helper functions as a “coverage hole” if the test’s goal is to isolate and verify only the validate-layer (or another single unit of behavior). If the stubbed helper’s actual logic is exercised in dedicated tests at the helper/service layer (e.g., test_*_helper* / test_librenms_id.py), it’s acceptable for the validate-layer test to control helper outputs (via side_effect/return values) and assert the validate-layer mapping/selection logic only. Flag only when the stub hides untested logic that should belong to the unit under test (i.e., the test asserts behavior from the helper without actually verifying the unit’s own responsibility).

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-06-15T18:49:04.201Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 104
File: netbox_librenms_plugin/tests/test_coverage_actions.py:1866-1870
Timestamp: 2026-06-15T18:49:04.201Z
Learning: When reviewing tests related to the LibreNMS device ID migration flow (e.g., `migrate_librenms_id` / `migrate_legacy_librenms_id`), do not require `validation["librenms_id_needs_migration"] == True` solely for test setup. That flag is only used for UI visibility in `device_status.py` / `device_validation_details.html`; the backend migration action is gated by the instance’s legacy raw value (`custom_field_data["librenms_id"]` matching the active LibreNMS device id) plus the `serial_confirmed` or `force` condition. If the test already pins/executes migration by asserting the migration function was called with the locked instance (or otherwise directly forces execution), it should be acceptable even when `librenms_id_needs_migration` is not set to True.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-06-17T07:31:54.849Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 104
File: netbox_librenms_plugin/tests/test_librenms_api.py:2258-2258
Timestamp: 2026-06-17T07:31:54.849Z
Learning: When reviewing Python test code in netbox_librenms_plugin/tests, treat “develop-owned” scaffold lines as off-limits for in-PR rewrites. A line is “develop-owned” if `git blame` for that line attributes it to a commit that is an ancestor of `origin/develop` (i.e., the commit is contained in `origin/develop`). For such lines, reviewers should acknowledge the findings as valid but defer the change by creating/using a follow-up issue targeting the `develop` branch (e.g., `#112`), rather than requesting modifications in the current feature/PR stack.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-06-19T14:03:09.440Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 113
File: netbox_librenms_plugin/tests/test_coverage_base_views2.py:555-567
Timestamp: 2026-06-19T14:03:09.440Z
Learning: In tests under netbox_librenms_plugin/tests, don’t rely on “pure” MagicMock setups that stub chained calls like `interfaces.filter.return_value.first.return_value` when the code under test is supposed to distinguish between (1) a librenms_id custom-field lookup and (2) a name-based fallback lookup. If the mock returns the same interface regardless of filter arguments, the test cannot detect which lookup path matched (renaming variables like `remote_port` doesn’t fix this). Use a real-DB hardening test instead: create/seed a `remote_port` value that is deliberately different from the actual interface name so only the librenms_id CF lookup can produce a match. If an existing MagicMock-masked test file is develop-inherited (identical on origin/develop), don’t modify it in feature PRs; add a new real-DB hardening test file (e.g., `test_enrich_remote_port_realdb.py`) on the develop-targeted branch.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-06-25T07:07:59.192Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 114
File: netbox_librenms_plugin/tests/test_oob_sync_review_fixes.py:0-0
Timestamp: 2026-06-25T07:07:59.192Z
Learning: When writing/adjusting tests (and any review/test helper code) that interact with NetBox’s custom User model, do not assume the user model has an `is_staff` field. If you need to check or set user privileges/eligibility, use fields that are known to exist on the NetBox User model (e.g., `is_superuser` and `is_active`) instead. Avoid setting `is_staff` (it can raise Django `FieldError` if that field doesn’t exist on the custom model).

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-07-02T21:46:46.384Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 116
File: netbox_librenms_plugin/tests/test_coverage_bulk_import.py:132-140
Timestamp: 2026-07-02T21:46:46.384Z
Learning: In netbox_librenms_plugin/tests, keep the `_stub_norm_preload` autouse fixture (which patches `netbox_librenms_plugin.import_utils.bulk_import.preload_normalization_rules` to return `{}`) intentionally duplicated per mock-based test class/module. Do NOT hoist it into a global `conftest.py` autouse fixture, because that would apply the patch repo-wide and mask the real `preload_normalization_rules` behavior in real-DB tests that are meant to exercise it (see issue `#90`). If you need this patch, scope it to the specific mock-based tests that should stub normalization rules.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-04-01T15:55:42.180Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 50
File: netbox_librenms_plugin/tests/test_coverage_actions.py:88-171
Timestamp: 2026-04-01T15:55:42.180Z
Learning: When unit/integration testing actions that indirectly use a function imported at module import time, patch the function where it is *used* (the consumer’s import path), e.g. `netbox_librenms_plugin.views.imports.actions.resolve_naming_preferences`, rather than its original definition. For tests that target the function itself directly, patch the original dependency/definition (e.g. `netbox_librenms_plugin.utils.get_user_pref` or patch `resolve_naming_preferences` at `netbox_librenms_plugin.utils`) so the function under test sees the mocked behavior.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-05-05T09:46:17.700Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 58
File: netbox_librenms_plugin/tests/test_vm_operations.py:46-46
Timestamp: 2026-05-05T09:46:17.700Z
Learning: When the code under test performs *lazy imports* inside function bodies (i.e., the imported symbol is not bound at the module scope), mock/patch the *source module path that the function imports from*, not the consumer module path. The correct patch target is where the imported name is resolved at runtime (e.g., `virtualization.models.VirtualMachine`), because patching `netbox_librenms_plugin.import_utils.vm_operations.VirtualMachine` can fail with `AttributeError` since `VirtualMachine` is never a `vm_operations` module attribute.

Applied to files:

  • netbox_librenms_plugin/tests/test_import_validation_helpers.py
  • netbox_librenms_plugin/tests/test_collisions.py
  • netbox_librenms_plugin/tests/test_cable_verify.py
  • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
  • netbox_librenms_plugin/tests/test_background_jobs.py
📚 Learning: 2026-03-08T09:30:45.499Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 21
File: netbox_librenms_plugin/views/imports/actions.py:1031-1035
Timestamp: 2026-03-08T09:30:45.499Z
Learning: In netbox_librenms_plugin/views/imports/actions.py, ensure that DeviceConflictActionView.post() explicitly rejects boolean values for librenms_id via isinstance(librenms_id, bool) before coercing to int, returning HTTP 400. Do not remove or consolidate this pre-coercion boolean check. This guard is intentional and consistent with the similar bool-guard pattern used in set_librenms_device_id, get_librenms_device_id, and find_by_librenms_id; preserve this behavior to avoid ambiguity and misinterpretation of truthy/falsey booleans.

Applied to files:

  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-04-15T12:38:49.280Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 50
File: netbox_librenms_plugin/views/base/modules_view.py:133-141
Timestamp: 2026-04-15T12:38:49.280Z
Learning: Do not require or flag an explicit `permission_required = PERM_VIEW_PLUGIN` on views that inherit from `LibreNMSPermissionMixin` (e.g., those ultimately including `BaseModuleTableView` in `views/base/modules_view.py`). `LibreNMSPermissionMixin` is the authoritative place where `permission_required` is set to `PERM_VIEW_PLUGIN`; inherited values are already enforced via the mixin, making a redundant override unnecessary.

Applied to files:

  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-05-17T11:32:40.631Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 76
File: netbox_librenms_plugin/views/object_sync/devices.py:178-180
Timestamp: 2026-05-17T11:32:40.631Z
Learning: In this plugin’s JSON endpoint views (e.g., NetBox view classes/functions under netbox_librenms_plugin/views/**), do not require a try/except JSONDecodeError around `json.loads(request.body)` by default if the endpoint is already protected by CSRF + session authentication and has object-level permission gates. Treat missing JSONDecodeError handling as acceptable when the only expected downside is a noisy log line. If you identify any additional security or availability impact from invalid JSON (e.g., unhandled exceptions leading to user-visible 500s, potential DoS amplification, or lack of appropriate throttling/validation), then flag it and recommend adding guarded parsing/validation.

Applied to files:

  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-07-02T13:36:15.226Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 116
File: netbox_librenms_plugin/views/base/interfaces_view.py:226-226
Timestamp: 2026-07-02T13:36:15.226Z
Learning: When reviewing netbox-librenms-plugin view code that handles POSTed `server_key`, treat the plugin-wide convention as intentional: if the posted `server_key` is not present in `LibreNMSAPI.get_available_servers()`, the request should fall back to the currently configured default/active server key (not reject/fail-closed and not treat it as an error for that single view). Do not flag individual instances of this fallback pattern as incorrect “invalid server_key” validation. Any change to fail-closed behavior must be a coordinated cross-cutting change applied uniformly across all affected sites/views, not a one-view patch.

Applied to files:

  • netbox_librenms_plugin/views/imports/actions.py
📚 Learning: 2026-06-01T13:32:29.984Z
Learnt from: marcinpsk
Repo: marcinpsk/netbox-librenms-plugin PR: 79
File: netbox_librenms_plugin/views/imports/list.py:0-0
Timestamp: 2026-06-01T13:32:29.984Z
Learning: When reviewing the NetBox LibreNMS plugin’s import/IPAM code (including files under netbox_librenms_plugin/**/imports/), do not flag issues about missing propagation of the `auto_create_ipam` flag (or `resolve_auto_create_ipam()` usage) into cache keys or `FilterDevicesJob` payloads. The `auto_create_ipam` feature was removed entirely during the IPAM rework merged to `develop` (PR `#303`), so the absence of this flag/payload data in current code is expected.

Applied to files:

  • netbox_librenms_plugin/views/imports/actions.py
🪛 ast-grep (0.44.1)
netbox_librenms_plugin/views/imports/actions.py

[error] 817-821: Avoid HTML built in strings
Context: render(
request,
"netbox_librenms_plugin/htmx/bulk_import_collision.html",
{"collisions": collisions},
)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(html-string-from-parameters)


[error] 994-998: Avoid HTML built in strings
Context: render(
request,
"netbox_librenms_plugin/htmx/bulk_import_collision.html",
{"error_message": msg},
)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(html-string-from-parameters)


[error] 1004-1008: Avoid HTML built in strings
Context: render(
request,
"netbox_librenms_plugin/htmx/bulk_import_collision.html",
{"collisions": collisions},
)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(html-string-from-parameters)

🪛 HTMLHint (1.9.2)
netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)


[error] 11-11: Special characters must be escaped : [ < ].

(spec-char-escape)


[error] 11-11: Special characters must be escaped : [ > ].

(spec-char-escape)


[error] 10-10: Tag must be paired, missing: [ ], start tag match failed [

  • ] on line 10.

    (tag-pair)


    [error] 12-12: Tag must be paired, no start tag: [

  • ]

    (tag-pair)


    [error] 14-14: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 14-14: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 14-14: Tag must be paired, no start tag: [ ]

    (tag-pair)


    [error] 17-17: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 17-17: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 17-17: Tag must be paired, no start tag: [ ]

    (tag-pair)


    [error] 20-20: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 20-20: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 20-20: Tag must be paired, no start tag: [ ]

    (tag-pair)


    [error] 23-23: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 23-23: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 23-23: Tag must be paired, no start tag: [ ]

    (tag-pair)


    [error] 25-25: Tag must be paired, no start tag: [ ]

    (tag-pair)

    netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html

    [error] 1-1: Doctype must be declared before any non-comment content.

    (doctype-first)

    netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html

    [error] 1-1: Doctype must be declared before any non-comment content.

    (doctype-first)


    [error] 11-11: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 11-11: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 10-10: Tag must be paired, missing: [ ], start tag match failed [

  • ] on line 10.

    (tag-pair)


    [error] 12-12: Tag must be paired, no start tag: [

  • ]

    (tag-pair)


    [error] 14-14: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 14-14: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 14-14: Tag must be paired, no start tag: [ ]

    (tag-pair)


    [error] 17-17: Special characters must be escaped : [ < ].

    (spec-char-escape)


    [error] 17-17: Special characters must be escaped : [ > ].

    (spec-char-escape)


    [error] 17-17: Tag must be paired, no start tag: [ ]

    (tag-pair)


    [error] 19-19: Tag must be paired, no start tag: [ ]

    (tag-pair)

    🪛 markdownlint-cli2 (0.22.1)
    docs/usage_tips/interface_mappings.md

    [warning] 32-32: Images should have alternate text (alt text)

    (MD045, no-alt-text)

    @marcinpsk
    marcinpsk force-pushed the feat/nav-consolidation branch from f926c98 to f4a5dc5 Compare July 9, 2026 06:32
    @marcinpsk

    Copy link
    Copy Markdown
    Owner Author

    @coderabbitai review

    @coderabbitai

    coderabbitai Bot commented Jul 9, 2026

    Copy link
    Copy Markdown
    ✅ Action performed

    Review finished.

    Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

    @coderabbitai coderabbitai 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.

    Actionable comments posted: 3

    🤖 Prompt for all review comments with AI agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    Inline comments:
    In `@netbox_librenms_plugin/import_utils/bulk_import.py`:
    - Around line 116-124: Wrap the `api.get_device_info()` call inside
    `detect_collisions_for_device_ids` with exception handling so transient
    LibreNMS/network errors do not escape the loop. If `get_device_info` raises,
    treat that `device_id` the same as a failed `(success, libre_device)` lookup by
    appending it to `unresolved_ids` and continuing. Keep the fail-closed behavior
    consistent with the surrounding cache lookup and collision-check logic in
    `bulk_import.py`.
    
    In
    `@netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html`:
    - Around line 9-25: Update the active state handling in _mapping_tabs.html so
    each nav-link in the mapping tab list includes aria-current="page" whenever
    mapping_tab_active matches that tab’s identifier (for example, the links in the
    Interface Mappings, Device Type Mappings, Module Type Mappings, Module Bay
    Mappings, and Platform Mappings entries). Keep the existing active class logic
    and make the aria-current attribute conditional on the same mapping_tab_active
    checks so screen readers announce the current page correctly.
    
    In `@netbox_librenms_plugin/tests/test_coverage_bulk_import.py`:
    - Around line 2758-2888: The real-DB coverage in
    TestDetectCollisionsForDeviceIds never exercises VM handling, so it can’t catch
    regressions where detect_collisions_for_device_ids or validate_device_for_import
    mishandles import_as_vm. Add at least one test in this class that uses a VM
    fixture/helper instead of make_device, and invoke
    detect_collisions_for_device_ids with a cache and sync_options that force the VM
    path. Keep the existing collision/unresolved assertions, but make sure the new
    case proves VM rows are validated and collision-checked correctly.
    
    🪄 Autofix (Beta)

    Fix all unresolved CodeRabbit comments on this PR:

    • Push a commit to this branch (recommended)
    • Create a new PR with the fixes

    ℹ️ Review info
    ⚙️ Run configuration

    Configuration used: Repository UI (base), Organization UI (inherited)

    Review profile: ASSERTIVE

    Plan: Pro

    Run ID: 6778dff7-de76-4404-8e59-b4d86a2414a2

    📥 Commits

    Reviewing files that changed from the base of the PR and between f926c98 and f4a5dc5.

    📒 Files selected for processing (34)
    • docs/usage_tips/interface_mappings.md
    • docs/usage_tips/mapping_rules.md
    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_coverage_actions.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/tests/test_coverage_device_operations.py
    • netbox_librenms_plugin/tests/test_import_utils.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_migrate_views.py
    • netbox_librenms_plugin/tests/test_rules_patterns_navigation.py
    • netbox_librenms_plugin/tests/test_utils.py
    • netbox_librenms_plugin/utils.py
    • netbox_librenms_plugin/views/imports/actions.py
    • netbox_librenms_plugin/views/sync/modules.py
    📜 Review details
    🧰 Additional context used
    📓 Path-based instructions (9)
    netbox_librenms_plugin/templates/**/*.html

    📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

    netbox_librenms_plugin/templates/**/*.html: HTMX 2.x is the primary async layer. Table row updates should return <tr hx-swap-oob="true">.
    Avoid outerHTML swaps in HTMX; use OOB or targeted innerHTML swaps to keep table layout intact.
    Do not reintroduce data-bs-toggle or duplicate modal IDs in modal implementation.
    Keep <select class="device-role-select"> markup stable to preserve JavaScript hook-up for TomSelect decorators.
    Do not re-add table-responsive wrappers as their removal was deliberate to prevent dropdown clipping.
    Templates live in templates/netbox_librenms_plugin/; reuse and includes go under inc/ subdirectory.

    Files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    netbox_librenms_plugin/**/*.{html,js}

    📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

    All HTMX requests and fetch() calls must include a CSRF token. Prefer extracting from hidden form input via document.querySelector('[name=csrfmiddlewaretoken]').value rather than cookie-based approach.

    Files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    netbox_librenms_plugin/**/*.{html,css}

    📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

    Styling assumes Tabler defaults for the netbox_librenms_plugin frontend.

    Files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    **/import_utils/__init__.py

    📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

    import_utils/ is a package; the __init__.py must re-export key functions so callers can use from import_utils import ... for functions from filters.py, device_operations.py, vm_operations.py, cache.py, permissions.py, and virtual_chassis.py

    Files:

    • netbox_librenms_plugin/import_utils/__init__.py
    **/*.py

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    When building HttpResponse from Django-template-rendered HTML in views, use format_html() to compose the envelope and mark_safe() on the inner HTML to clear CodeQL py/reflected-xss false positives. Example: format_html('<div id="target" hx-swap-oob="innerHTML">{}</div>', mark_safe(modal_html))

    Files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    **/jobs.py

    📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

    **/jobs.py: Background jobs must use NetBox's JobRunner base class (netbox.jobs.JobRunner) for long-running operations like device filtering with VC detection
    Use Job UUID (job.job_id) for RQ API endpoints (/api/core/background-tasks/{uuid}/) and Job PK (job.pk) for database endpoints and result loading
    RQ status values are: queued, started, finished, stopped, failed (NOT completed). Database Job status values are: pending, scheduled, running, completed, failed, errored (NO cancelled status exists)
    Check rq_job.is_stopped or rq_job.is_failed flags in Redis for cancellation detection, not database status
    FilterDevicesJob background job — filters devices with VC detection. job.data keys: device_ids, total_processed, filters, server_key, vc_detection_enabled, cache_timeout, cached_at, completed. Devices cached individually via shared cache keys from get_validated_device_cache_key()
    ImportDevicesJob background job — imports devices/VMs. Calls bulk_import_devices_shared() for devices and bulk_import_vms() for VMs. job.data keys: imported_device_pks, imported_vm_pks, imported_libre_device_ids, imported_libre_vm_ids, server_key, total, success_count, failed_count, skipped_count, virtual_chassis_created, errors, completed
    Both synchronous and background modes must use get_validated_device_cache_key() from import_utils.py to generate cache keys, ensuring _load_job_results() in the list view can retrieve devices regardless of which mode produced them. Never hardcode cache key formats; always use the helper functions
    Use standalone permission helper functions from import_utils.py for permission checks inside job code: check_user_permissions(user, permissions)(bool, missing_list) and require_permissions(user, permissions, action_description) — raises PermissionDenied. Background jobs run outside view context and cannot use view mixins

    Files:

    • netbox_librenms_plugin/jobs.py
    netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/*.html

    📄 CodeRabbit inference engine (.github/instructions/frontend.instructions.md)

    HTMX fragments live in templates/netbox_librenms_plugin/htmx/ including: device_import_row.html, device_validation_details.html, device_vc_details.html, bulk_import_confirm.html.

    Files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    **/import_validation_helpers.py

    📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

    **/import_validation_helpers.py: import_validation_helpers.py must provide validation state mutation functions: apply_role_to_validation(), apply_cluster_to_validation(), apply_rack_to_validation() for updating validation state when user selects a role/cluster/rack, and remove_validation_issue(), recalculate_validation_status() for maintaining issue list and overall status
    import_validation_helpers.py must provide helper functions: fetch_model_by_id() and extract_device_selections() for reading form data

    Files:

    • netbox_librenms_plugin/import_validation_helpers.py
    **/import_utils/device_operations.py

    📄 CodeRabbit inference engine (.github/instructions/background-jobs.instructions.md)

    device_operations.py must export: validate_device_for_import(device, ...) and bulk_import_devices_shared(devices, user, ...)

    Files:

    • netbox_librenms_plugin/import_utils/device_operations.py
    🧠 Learnings (26)
    📚 Learning: 2026-03-13T11:16:36.294Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 22
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html:136-137
    Timestamp: 2026-03-13T11:16:36.294Z
    Learning: In Django templates under netbox_librenms_plugin/templates/**/*.html, do not suggest adding explicit parentheses to {% if %} expressions for readability. The project favors compact expressions using implicit operator precedence (and binds tighter than or). Treat parentheses as cosmetic and avoid guidance to insert them for style reasons.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-05-01T08:25:06.260Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 50
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html:245-246
    Timestamp: 2026-05-01T08:25:06.260Z
    Learning: In netbox_librenms_plugin template HTML/HTMX code, only require an X-CSRFToken header for state-changing requests made via fetch() or HTMX (POST, PUT, PATCH, DELETE). Do not require X-CSRFToken on read-only fetch() GET calls (e.g., autocomplete/lookup endpoints like dcim-api:devicetype-list); Django/DRF exempt GET requests from CSRF validation. Therefore, code reviews should not flag missing CSRF headers on GET fetch() calls used for lookups/autocomplete.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-06-14T22:58:16.581Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 87
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html:196-196
    Timestamp: 2026-06-14T22:58:16.581Z
    Learning: In Django template files under netbox_librenms_plugin/templates/**/*.html, do NOT flag template expressions like accessing a chained attribute on a possibly-None variable (e.g., `librenms_sync_device.pk` when `librenms_sync_device` may be None) as a NullPointerError/AttributeError. Django’s template attribute lookup resolves failed lookups to `TEMPLATE_STRING_IF_INVALID` (empty string by default), so comparisons such as `object.pk == librenms_sync_device.pk` will evaluate against `''` and safely result in False rather than raising a template error.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-03-13T20:03:16.435Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 22
    File: netbox_librenms_plugin/tests/test_coverage_api2.py:319-355
    Timestamp: 2026-03-13T20:03:16.435Z
    Learning: Do not propose replacing server_info with module_sync.server_key in the templates located under netbox_librenms_plugin/templates/netbox_librenms_plugin (specifically _module_sync.html and inc/_module_sync.html). These templates rely on server_info being present in the parent template context (librenms_sync_base.html) and server_key may be absent on initial load. Treat the correct usage of server_info for populating the value as the intended pattern; only flag issues if server_key is incorrectly used in these templates. This guideline applies to all files under the templates path for this plugin.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-06-01T20:22:57.975Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 79
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html:695-700
    Timestamp: 2026-06-01T20:22:57.975Z
    Learning: Do not recommend adding or propagating the removed `auto_create_ipam` toggle/preference via HTMX (e.g., `hx-include="`#auto-create-ipam-toggle`"`) or by introducing hidden `auto_create_ipam` inputs in out-of-band (OOB) / “promote” POST forms. Since the `auto_create_ipam` feature has been removed from the import page, any review suggestions attempting to wire it into `device_validation_details.html` or other import-flow templates should be ignored.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-06-14T22:58:16.581Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 87
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html:196-196
    Timestamp: 2026-06-14T22:58:16.581Z
    Learning: In Django templates, the `{% if %}` tag does not support parenthetical grouping. Do not suggest adding parentheses like `{% if (not x) %}` or `{% if (a or b) %}`—these can raise `TemplateSyntaxError` (e.g., “Could not parse the remainder”). Instead, express the logic using Django template operator precedence rules (not binds tighter than and, and binds tighter than or) and refactor (e.g., via separate conditions/`{% if %}` blocks) when precedence alone can’t express the intended grouping.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_mapping_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-07-07T22:23:42.118Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 104
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/portstacklagpattern.html:5-28
    Timestamp: 2026-07-07T22:23:42.118Z
    Learning: In netbox-librenms-plugin, do not flag NetBox object detail templates for the config/mapping/rule/pattern-style models when they omit including `extras/inc/custom_fields_panel.html` and `extras/inc/tags_panel.html` (even though these models expose `custom_field_data` and/or `TaggableManager`). This omission is a deliberate, plugin-wide UI convention. If changing this behavior (adding custom-fields/tags panels), require a single coordinated proposal spanning the relevant forms and all detail templates together rather than treating individual missing includes as a regression.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html
    • netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html
    📚 Learning: 2026-03-07T22:46:57.537Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 21
    File: netbox_librenms_plugin/tests/test_librenms_id.py:184-184
    Timestamp: 2026-03-07T22:46:57.537Z
    Learning: Do not flag or request style-only changes (such as removing redundant imports or cosmetic cleanup) in Python code reviews. Focus on issues that affect correctness, functionality, or security. This guideline applies to Python files across the repository, including tests (e.g., netbox_librenms_plugin/tests/test_librenms_id.py).
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-03-08T13:09:49.031Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 22
    File: netbox_librenms_plugin/forms.py:75-79
    Timestamp: 2026-03-08T13:09:49.031Z
    Learning: In multi-server caching within the codebase, ensure poller group choices and similar cache discriminators use api.server_key as the cache key component (e.g., cache_key = f"librenms_poller_group_choices_{api.server_key}") rather than api.librenms_url. This aligns with the fixed approach seen in commit bf37f07 and with other modules. Apply this pattern consistently to Python files under netbox_librenms_plugin (and similar multi-server cache keys) to maintain correct cross-server caching behavior.
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-05-05T09:51:15.707Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 58
    File: netbox_librenms_plugin/views/base/modules_view.py:311-313
    Timestamp: 2026-05-05T09:51:15.707Z
    Learning: In this repository, if you see `timezone.timedelta` used from `django.utils.timezone`, do not request a diff to replace it with `datetime.timedelta` solely on style grounds. This usage is functionally correct because Django exposes `timedelta` via `django.utils.timezone`; treat this as intentional and avoid churn unless there is evidence of changed/incorrect behavior (e.g., `timezone` is not Django’s module or `timedelta` is unavailable).
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-06-01T13:35:47.228Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 79
    File: netbox_librenms_plugin/views/sync/migrate.py:177-181
    Timestamp: 2026-06-01T13:35:47.228Z
    Learning: When reviewing this plugin’s permission checks, note that `check_object_permissions` / `NetBoxObjectPermissionMixin` enforce only **model-level** permissions: they call `request.user.has_perm(perm)` without any object/row instance, and the plugin does not currently implement per-object (row-level) permission scoping. Therefore, do **not** flag “missing winner-side/per-object object-permission checks” in sync/migrate views (or elsewhere in the plugin) as a defect; per-object permission scoping is an intentional plugin-wide design gap to be addressed in a dedicated future PR.
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-07-01T16:41:50.451Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 113
    File: netbox_librenms_plugin/tables/cables.py:62-76
    Timestamp: 2026-07-01T16:41:50.451Z
    Learning: When rendering Bootstrap/Tabler badges in this repo (netbox-librenms-plugin), always pair a solid background utility `bg-*` with an explicit text utility that provides appropriate contrast (e.g., `bg-danger` + `text-white`, `bg-warning` + `text-dark`, `bg-purple` + `text-white`). Follow the existing badge contrast convention enforced by `netbox_librenms_plugin/tests/test_badge_contrast.py`. Do not suggest Tabler’s semantic `text-*-fg` token classes (e.g., `text-purple-fg`), since they are not used in this codebase and are not covered by the contrast test.
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/jobs.py
    • netbox_librenms_plugin/navigation.py
    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    • netbox_librenms_plugin/import_validation_helpers.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-03-08T14:17:28.826Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 22
    File: netbox_librenms_plugin/import_utils/virtual_chassis.py:73-80
    Timestamp: 2026-03-08T14:17:28.826Z
    Learning: In Python code, when a lookup returns None (including API failures), implement negative caching by storing an empty result with a configurable TTL (default 5 minutes). Document the TTL and ensure a force_refresh=True bypasses the cache for manual re-fetch actions. Do not treat caching None/empty results on API failure as a bug if this mirrors existing patterns (e.g., get_device_with_server caching None on not-found). Apply this guidance to files within netbox_librenms_plugin/import_utils where similar inventory/API lookups occur.
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-03-09T20:10:48.502Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 25
    File: netbox_librenms_plugin/import_utils/device_operations.py:496-575
    Timestamp: 2026-03-09T20:10:48.502Z
    Learning: In netbox_librenms_plugin/import_utils/device_operations.py, in validate_device_for_import(), ensure both the cluster-required blocker (VM path) and the device_role-required blocker (device path) are guarded with if not result.get('existing_device') to ensure create-time prerequisites are only appended for new imports, not for link/update flows; keep available_roles and available_clusters populated for both new and existing-device cases so UI dropdowns function correctly on update views.
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-05-22T19:37:46.167Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 79
    File: netbox_librenms_plugin/tests/test_import_utils.py:1763-1766
    Timestamp: 2026-05-22T19:37:46.167Z
    Learning: In `validate_device_for_import` (device operations / serial-diff name-resolution logic), preserve the following result contract so the UI/test expectations remain stable:
    - If `serial_action` is determined via a serial match AND the existing device has NO OOB/LibreNMS link, set `serial_action` to `"oob_candidate"` and ensure `promote_to_host` is NOT present in the returned dict.
    - Populate `promote_to_host` only when the existing device already has an OOB/LibreNMS link (i.e., a host id is available to inherit from); otherwise omit the key.
    - Always include `serial_role_choice_available` in the returned dict, defaulting to `False` (baseline) even when other resolution outcomes do not enable it.
    

    Applied to files:

    • netbox_librenms_plugin/import_utils/__init__.py
    • netbox_librenms_plugin/import_utils/collisions.py
    • netbox_librenms_plugin/import_utils/device_operations.py
    • netbox_librenms_plugin/import_utils/bulk_import.py
    📚 Learning: 2026-05-25T21:48:19.264Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 79
    File: netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/_platform_mapping_form.html:12-12
    Timestamp: 2026-05-25T21:48:19.264Z
    Learning: In this plugin’s HTMX form templates, `hx-include` selectors that target toggle preference wrapper `<span>` element IDs (e.g., `#use-sysname-toggle`, `#strip-domain-toggle`, `#auto-create-ipam-toggle`) are intentional. Those wrapper spans contain both the hidden `off` fallback input and the checkbox; HTMX must include the wrapper so the correct value is serialized, including the unchecked/off state. Do not recommend changing `hx-include` to the checkbox IDs with the `-cb` suffix, since it would omit the hidden fallback and break unchecked/off state submission.
    

    Applied to files:

    • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html
    📚 Learning: 2026-03-27T02:04:22.276Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 22
    File: netbox_librenms_plugin/tests/test_coverage_api.py:893-939
    Timestamp: 2026-03-27T02:04:22.276Z
    Learning: For unit tests in this repo (e.g., coverage API tests), when testing a happy-path call like `add_device()`, assert both the success flag and the expected success message (e.g., `assert ok is True` and `assert msg == "Device added successfully."`). This ensures the test fails if `add_device()` returns `(False, ...)`. If a related assertion is explicitly tracked as a known deferred follow-up for a prior PR, do not treat the missing `ok is True` assertion as a new review finding in subsequent reviews.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-06-02T11:11:56.131Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 79
    File: netbox_librenms_plugin/tests/test_coverage_actions.py:4773-4776
    Timestamp: 2026-06-02T11:11:56.131Z
    Learning: When application code performs a function-local import inside a method body (e.g., `from utilities.permissions import get_permission_for_model`), unit tests should patch the original source attribute (`utilities.permissions.get_permission_for_model`). Do not patch the consumer module’s name (e.g., `netbox_librenms_plugin.views.imports.actions.get_permission_for_model`) unless the function is imported at module scope and exposed as a module attribute—local imports re-resolve the attribute at call time.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-06-02T20:43:51.604Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 79
    File: netbox_librenms_plugin/tests/test_coverage_device_operations.py:2466-2478
    Timestamp: 2026-06-02T20:43:51.604Z
    Learning: When reviewing tests under netbox_librenms_plugin/tests, don’t treat intentional stubs/mocks of lower-layer helper functions as a “coverage hole” if the test’s goal is to isolate and verify only the validate-layer (or another single unit of behavior). If the stubbed helper’s actual logic is exercised in dedicated tests at the helper/service layer (e.g., test_*_helper* / test_librenms_id.py), it’s acceptable for the validate-layer test to control helper outputs (via side_effect/return values) and assert the validate-layer mapping/selection logic only. Flag only when the stub hides untested logic that should belong to the unit under test (i.e., the test asserts behavior from the helper without actually verifying the unit’s own responsibility).
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-06-15T18:49:04.201Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 104
    File: netbox_librenms_plugin/tests/test_coverage_actions.py:1866-1870
    Timestamp: 2026-06-15T18:49:04.201Z
    Learning: When reviewing tests related to the LibreNMS device ID migration flow (e.g., `migrate_librenms_id` / `migrate_legacy_librenms_id`), do not require `validation["librenms_id_needs_migration"] == True` solely for test setup. That flag is only used for UI visibility in `device_status.py` / `device_validation_details.html`; the backend migration action is gated by the instance’s legacy raw value (`custom_field_data["librenms_id"]` matching the active LibreNMS device id) plus the `serial_confirmed` or `force` condition. If the test already pins/executes migration by asserting the migration function was called with the locked instance (or otherwise directly forces execution), it should be acceptable even when `librenms_id_needs_migration` is not set to True.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-06-17T07:31:54.849Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 104
    File: netbox_librenms_plugin/tests/test_librenms_api.py:2258-2258
    Timestamp: 2026-06-17T07:31:54.849Z
    Learning: When reviewing Python test code in netbox_librenms_plugin/tests, treat “develop-owned” scaffold lines as off-limits for in-PR rewrites. A line is “develop-owned” if `git blame` for that line attributes it to a commit that is an ancestor of `origin/develop` (i.e., the commit is contained in `origin/develop`). For such lines, reviewers should acknowledge the findings as valid but defer the change by creating/using a follow-up issue targeting the `develop` branch (e.g., `#112`), rather than requesting modifications in the current feature/PR stack.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-06-19T14:03:09.440Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 113
    File: netbox_librenms_plugin/tests/test_coverage_base_views2.py:555-567
    Timestamp: 2026-06-19T14:03:09.440Z
    Learning: In tests under netbox_librenms_plugin/tests, don’t rely on “pure” MagicMock setups that stub chained calls like `interfaces.filter.return_value.first.return_value` when the code under test is supposed to distinguish between (1) a librenms_id custom-field lookup and (2) a name-based fallback lookup. If the mock returns the same interface regardless of filter arguments, the test cannot detect which lookup path matched (renaming variables like `remote_port` doesn’t fix this). Use a real-DB hardening test instead: create/seed a `remote_port` value that is deliberately different from the actual interface name so only the librenms_id CF lookup can produce a match. If an existing MagicMock-masked test file is develop-inherited (identical on origin/develop), don’t modify it in feature PRs; add a new real-DB hardening test file (e.g., `test_enrich_remote_port_realdb.py`) on the develop-targeted branch.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-06-25T07:07:59.192Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 114
    File: netbox_librenms_plugin/tests/test_oob_sync_review_fixes.py:0-0
    Timestamp: 2026-06-25T07:07:59.192Z
    Learning: When writing/adjusting tests (and any review/test helper code) that interact with NetBox’s custom User model, do not assume the user model has an `is_staff` field. If you need to check or set user privileges/eligibility, use fields that are known to exist on the NetBox User model (e.g., `is_superuser` and `is_active`) instead. Avoid setting `is_staff` (it can raise Django `FieldError` if that field doesn’t exist on the custom model).
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-07-02T21:46:46.384Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 116
    File: netbox_librenms_plugin/tests/test_coverage_bulk_import.py:132-140
    Timestamp: 2026-07-02T21:46:46.384Z
    Learning: In netbox_librenms_plugin/tests, keep the `_stub_norm_preload` autouse fixture (which patches `netbox_librenms_plugin.import_utils.bulk_import.preload_normalization_rules` to return `{}`) intentionally duplicated per mock-based test class/module. Do NOT hoist it into a global `conftest.py` autouse fixture, because that would apply the patch repo-wide and mask the real `preload_normalization_rules` behavior in real-DB tests that are meant to exercise it (see issue `#90`). If you need this patch, scope it to the specific mock-based tests that should stub normalization rules.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-04-01T15:55:42.180Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 50
    File: netbox_librenms_plugin/tests/test_coverage_actions.py:88-171
    Timestamp: 2026-04-01T15:55:42.180Z
    Learning: When unit/integration testing actions that indirectly use a function imported at module import time, patch the function where it is *used* (the consumer’s import path), e.g. `netbox_librenms_plugin.views.imports.actions.resolve_naming_preferences`, rather than its original definition. For tests that target the function itself directly, patch the original dependency/definition (e.g. `netbox_librenms_plugin.utils.get_user_pref` or patch `resolve_naming_preferences` at `netbox_librenms_plugin.utils`) so the function under test sees the mocked behavior.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    📚 Learning: 2026-05-05T09:46:17.700Z
    Learnt from: marcinpsk
    Repo: marcinpsk/netbox-librenms-plugin PR: 58
    File: netbox_librenms_plugin/tests/test_vm_operations.py:46-46
    Timestamp: 2026-05-05T09:46:17.700Z
    Learning: When the code under test performs *lazy imports* inside function bodies (i.e., the imported symbol is not bound at the module scope), mock/patch the *source module path that the function imports from*, not the consumer module path. The correct patch target is where the imported name is resolved at runtime (e.g., `virtualization.models.VirtualMachine`), because patching `netbox_librenms_plugin.import_utils.vm_operations.VirtualMachine` can fail with `AttributeError` since `VirtualMachine` is never a `vm_operations` module attribute.
    

    Applied to files:

    • netbox_librenms_plugin/tests/test_background_jobs.py
    • netbox_librenms_plugin/tests/test_cable_verify.py
    • netbox_librenms_plugin/tests/test_collisions.py
    • netbox_librenms_plugin/tests/test_import_validation_helpers.py
    • netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    🔇 Additional comments (27)
    docs/usage_tips/interface_mappings.md (1)

    32-32: Add alt text to the screenshot. The image still has an empty alt field, so the accessibility/MD045 issue remains.

    netbox_librenms_plugin/templates/netbox_librenms_plugin/modulebaymapping_list.html (1)

    3-6: LGTM!

    Also applies to: 19-24

    netbox_librenms_plugin/templates/netbox_librenms_plugin/devicetypemapping_list.html (1)

    3-6: LGTM!

    Also applies to: 19-24

    netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html (1)

    3-6: LGTM!

    Also applies to: 19-24

    docs/usage_tips/mapping_rules.md (1)

    5-5: LGTM!

    netbox_librenms_plugin/import_utils/__init__.py (1)

    17-55: LGTM!

    netbox_librenms_plugin/templates/netbox_librenms_plugin/moduletypemapping_list.html (1)

    3-7: LGTM!

    netbox_librenms_plugin/templates/netbox_librenms_plugin/inventoryignorerule_list.html (1)

    3-7: LGTM!

    netbox_librenms_plugin/tests/test_coverage_bulk_import.py (1)

    968-1054: LGTM!

    netbox_librenms_plugin/import_validation_helpers.py (1)

    12-43: LGTM!

    netbox_librenms_plugin/import_utils/device_operations.py (2)

    887-893: LGTM!

    Also applies to: 931-931, 1027-1038


    1075-1078: 🎯 Functional Correctness

    Add a regression test for the primary-IP ambiguity early return. This branch skips the later site/device-type/role validation path and is easy to miss in this function.

    netbox_librenms_plugin/import_utils/bulk_import.py (2)

    475-502: LGTM!

    Also applies to: 587-599


    77-141: 🎯 Functional Correctness

    Confirm the caller split before threading import_as_vm here. This helper is device-scoped (device_ids, bulk_import_devices_shared); whether any VM import path reaches it is still unclear from the available context.

    netbox_librenms_plugin/templates/netbox_librenms_plugin/normalizationrule_list.html (1)

    3-7: LGTM!

    netbox_librenms_plugin/templates/netbox_librenms_plugin/carrierautoinstallrule_list.html (1)

    3-7: LGTM!

    netbox_librenms_plugin/templates/netbox_librenms_plugin/platformmapping_list.html (1)

    3-7: LGTM!

    netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_collision.html (1)

    1-80: LGTM!

    netbox_librenms_plugin/navigation.py (1)

    1-3: LGTM!

    Also applies to: 39-60

    netbox_librenms_plugin/jobs.py (1)

    189-189: Confirm the detect_collisions_for_device_ids contract

    jobs.py depends on detect_collisions_for_device_ids being re-exported from import_utils and returning a 2-tuple (collisions, unresolved) for the unpacking here.

    netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/_rules_patterns_tabs.html (1)

    1-19: LGTM!

    netbox_librenms_plugin/tests/test_background_jobs.py (1)

    375-396: LGTM!

    Also applies to: 434-450, 485-513, 523-527, 615-632, 708-722, 749-793, 795-837, 839-871

    netbox_librenms_plugin/tests/test_cable_verify.py (1)

    380-391: LGTM!

    Also applies to: 500-505

    netbox_librenms_plugin/import_utils/collisions.py (2)

    1-225: LGTM!


    18-18: 🗄️ Data Integrity & Integration

    Check import_utils/__init__.py exports detect_collisions_for_device_ids

    If callers import from netbox_librenms_plugin.import_utils, this helper needs to be re-exported there; include detect_bulk_collisions too if it is part of the public API.

    netbox_librenms_plugin/tests/test_collisions.py (1)

    1-429: LGTM!

    netbox_librenms_plugin/tests/test_import_validation_helpers.py (1)

    388-506: LGTM!

    Also applies to: 525-654, 701-721

    Comment thread netbox_librenms_plugin/import_utils/bulk_import.py Outdated
    Comment thread netbox_librenms_plugin/tests/test_coverage_bulk_import.py
    @marcinpsk
    marcinpsk force-pushed the feat/nav-consolidation branch from f4a5dc5 to 4a952ce Compare July 9, 2026 23:38
    @marcinpsk

    Copy link
    Copy Markdown
    Owner Author

    @coderabbitai review

    @coderabbitai

    coderabbitai Bot commented Jul 10, 2026

    Copy link
    Copy Markdown
    ✅ Action performed

    Review finished.

    Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

    @marcinpsk
    marcinpsk force-pushed the feat/nav-consolidation branch from 4a952ce to eedcec7 Compare July 10, 2026 07:59
    …ct literal
    
    The single-string assertion coupled the switcher tests to exact attribute
    order/spacing — inserting e.g. role="tab" between attributes would fail them
    with the pill still correctly active. Extract the aria-current pill and assert
    the active class and href independently, keeping the exactly-one
    aria-current="page" contract.
    The plugin registers a top-level PluginMenu labelled LibreNMS; the page
    still directed readers to a 'Netbox Librenms Plugin' entry under the
    generic Plugins section.
    @marcinpsk
    marcinpsk force-pushed the feat/nav-consolidation branch from 14ea70c to bad417a Compare August 9, 2026 07:00
    Comment thread netbox_librenms_plugin/tests/test_validation_template_server_key.py
    @marcinpsk

    Copy link
    Copy Markdown
    Owner Author

    @coderabbitai review

    @coderabbitai

    coderabbitai Bot commented Aug 9, 2026

    Copy link
    Copy Markdown

    Rate Limit Exceeded

    @marcinpsk have exceeded the limit for the number of chat messages per hour. Please wait 6 minutes and 2 seconds before sending another message.

    @marcinpsk

    Copy link
    Copy Markdown
    Owner Author

    @coderabbitai review

    @coderabbitai

    coderabbitai Bot commented Aug 9, 2026

    Copy link
    Copy Markdown
    ✅ Action performed

    Review finished.

    Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

    Labels

    None yet

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    1 participant