Skip to content

fix: code quality improvements and bug fixes - #48

Merged
marcinpsk merged 10 commits into
developfrom
pr/code-quality-fixes
Apr 14, 2026
Merged

fix: code quality improvements and bug fixes#48
marcinpsk merged 10 commits into
developfrom
pr/code-quality-fixes

Conversation

@marcinpsk

@marcinpsk marcinpsk commented Mar 31, 2026

Copy link
Copy Markdown
Owner

Summary

Bug fixes and code quality improvements to the import pipeline, job cancellation, API views, and templates. No new features or schema changes. Addresses issues found during review after the multi-server merge.

Motivation / Problem

  • Bug: _is_job_cancelled fell back to DB status when Redis was unavailable, which could produce false cancellations
  • Bug: vc_detection_enabled defaulted to True when sync_options was absent, silently enabling VC detection on all imports
  • Bug: change_virtualmachine incorrectly included in required permissions — import only creates VMs, never updates them
  • Bug: BulkImportDevicesView read vc_detection_enabled from GET params instead of the POSTed confirmation form
  • Bug: Paginator template used wrong attribute path (page.* instead of table.page.*)
  • Bug: device_validation_details.html had wrong condition for existing-device check and VM model name
  • Bug: Existing devices on re-import accumulated spurious site/device-type validation issues
  • Bug: __init__.py startup migration for librenms_id custom field didn't use per-alias DB routing (multi-DB setups)
  • Refactor: _is_job_cancelled extracted as a shared helper (was duplicated between bulk_import and vm_operations)
  • Refactor: _resolve_naming_preferences duplicate in actions.py removed; now imports shared resolve_naming_preferences from utils
  • Refactor: Job names in api/views.py now sourced from FilterDevicesJob/ImportDevicesJob Meta rather than hardcoded strings
  • Refactor: NoSuchJobError caught explicitly in API views instead of bare Exception

Scope of Change

  • Sync/Import logic
  • Config / settings
  • Web UI / templates
  • Tests
  • Docs

How Was This Tested?

  • Unit tests: yes — updated and extended tests for cancellation logic (test_import_utils, test_vm_operations), permission checks, integer→JSON migration path (test_init), naming preference resolution (test_coverage_actions), and base view server key forwarding (test_coverage_base_views)
  • Manual testing: no — all changes are covered by unit tests or are template/doc fixes

Risk Assessment

  • vc_detection_enabled now defaults to off (was on) when sync_options is missing — safer, but callers that relied on the previous default will no longer get VC detection unless they explicitly pass the flag
  • Redis unavailable no longer cancels a running job — the job continues rather than aborting, which is less disruptive
  • Removing change_virtualmachine from required permissions allows users with only add_virtualmachine to run imports (correct behaviour)

Backwards Compatibility

  • No breaking changes to the external API, data model, or URL structure
  • The vc_detection_enabled default flip is a behavioural change in the internal import call chain, not in the user-facing UI flow

Other Notes

  • __init__.py now tracks which DB aliases have already run the startup migration via _executed_aliases to avoid duplicate execution in multi-DB setups — reviewers should verify the using(db_alias) call pattern is correct for their NetBox deployment

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed pagination navigation in interface listings
    • Corrected modal dialog visibility and accessibility
    • Fixed platform sync form appearance in validation views
    • Improved error handling and clearer failure responses for sync job status checks
  • Documentation

    • Renamed and updated custom field setup guide with steps for 0.4.3+ (including migration guidance)
  • Improvements

    • Better multi-database startup handling
    • Normalized VC-detection handling in bulk import (form now posts VC option)
    • Added filtering support for interface type mappings
    • Stricter input validation and parsing for device/interface identifiers and naming preferences

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

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

Run ID: 23078c2d-96a7-4962-91e2-5b2efb3c4f7e

📥 Commits

Reviewing files that changed from the base of the PR and between 1e4fa5e and d41ad0b.

📒 Files selected for processing (6)
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_confirm.html
  • netbox_librenms_plugin/tests/test_init.py
  • netbox_librenms_plugin/tests/test_vm_operations.py
  • netbox_librenms_plugin/views/imports/actions.py

📝 Walkthrough

Walkthrough

Per-DB-alias custom-field bootstrapping with integer→json migration; centralized RQ-based job-cancellation helper used across bulk device/VM import flows; removed standalone vc_detection parameter in favor of sync_options; tightened API job lookup/error handling; assorted template, view, JS, and test updates to align with these changes.

Changes

Cohort / File(s) Summary
Custom-field bootstrap & docs
netbox_librenms_plugin/__init__.py, docs/usage_tips/custom_field.md
Bootstrap now runs once per DB alias (_executed_aliases); ORM calls use using()/db_manager(); existing librenms_id type="integer" is migrated to "json" via save(using=alias, update_fields=["type"]); docs advise running migrations first.
Job cancellation & bulk import
netbox_librenms_plugin/import_utils/bulk_import.py, netbox_librenms_plugin/import_utils/vm_operations.py, netbox_librenms_plugin/import_utils/...
Introduce _is_job_cancelled(job) that relies on RQ state; remove inline try/DB-fallback cancellation checks; periodic checkpoints (1st and every Nth) call helper; VC-detection flag now derived from sync_options.
Import job signatures & orchestration
netbox_librenms_plugin/jobs.py, netbox_librenms_plugin/views/imports/actions.py
Removed vc_detection_enabled parameter from job/run paths; vc_detection_enabled now passed in sync_options; calls to naming-preference resolver switched to resolve_naming_preferences(request); added _DEVICE_ONLY_ACTIONS and reject those for VMs; recompute is_vm from validation results.
Validation & device operations
netbox_librenms_plugin/import_utils/device_operations.py, netbox_librenms_plugin/import_utils/vm_operations.py
Avoid appending certain Site/DeviceType blocking issues when an existing_device is present; VM/device import loops use centralized _is_job_cancelled checks.
API and sync-job status handling
netbox_librenms_plugin/api/views.py
InterfaceTypeMappingViewSet sets filterset_class; sync_job_status restricts DB job lookup to requesting user and specific job names; handle rq.exceptions.NoSuchJobError explicitly and only mark DB job failed if non-terminal.
Templates
netbox_librenms_plugin/templates/.../bulk_import_confirm.html, .../device_validation_details.html, .../inc/paginator.html
Add hidden vc_detection_enabled input and make its value stable across HTMX re-renders; relax platform-sync and VM-alert conditions; paginator prev/next now use table.page.*.
Cables view
netbox_librenms_plugin/views/base/cables_view.py
Build links_data with explicit loop using local_ports_map; initialize table explicitly in _prepare_context(); adjust server_key fallback assignment.
JS & UI helpers
netbox_librenms_plugin/static/.../librenms_sync.js
Add showModal/hideModal helpers; stricter device ID parsing; remove unused vars; restrict interface_name_field values; update HTMX modal label after settle.
Tests
netbox_librenms_plugin/tests/...
Tests updated for per-alias init handling and integer→json migration; use resolve_naming_preferences and utils.get_user_pref; mock rq.exceptions.NoSuchJobError; rewrite import/VM cancellation tests to patch _is_job_cancelled; add server_key forwarding test.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant JobRunner
  participant ImportUtils
  participant RQ
  participant Database

  User->>JobRunner: enqueue ImportDevicesJob(sync_options)
  JobRunner->>ImportUtils: run bulk_import_devices_shared(job, sync_options)
  loop per checkpoint (first and every 5th)
    ImportUtils->>RQ: fetch job (_is_job_cancelled)
    alt RQ job found
      RQ-->>ImportUtils: job state (failed/errored/stopped/completed)
      ImportUtils->>ImportUtils: if cancelled -> break loop
    else NoSuchJobError / RQ error
      RQ-->>ImportUtils: NoSuchJobError / error
      ImportUtils->>Database: (optional) inspect DB Job.status
      Database-->>ImportUtils: job.status
      ImportUtils->>ImportUtils: decide cancelled? (default: not cancelled)
    end
  end
  ImportUtils->>Database: persist per-device import results
  JobRunner-->>User: job finished / status updated
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through aliases, one by one,

turned integers to JSON till the migration was done,
jobs now check RQ with a single gentle call,
VC flags tucked neat inside sync_options for all,
a rabbit cheers — imports steady, standing tall.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is generic and does not clearly summarize the main changes; it uses vague terms like 'improvements' and 'bug fixes' without specifying what was actually fixed or improved. Provide a more specific title that highlights the primary change, such as 'fix: correct job cancellation logic and db routing in multi-server setup' or focus on the most impactful fix.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive and well-structured, addressing all template sections including Summary, Motivation/Problem, Scope, Testing, Risk Assessment, and Backwards Compatibility with specific details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr/code-quality-fixes

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 and usage tips.

@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

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/tests/test_init.py (1)

89-107: 🧹 Nitpick | 🔵 Trivial

Add a dedicated test for the integer→JSON migration branch.

The new behavior in _ensure_librenms_id_custom_field (cf.type == "integer"cf.save(using=alias, update_fields=["type"])) is not directly asserted yet. A targeted test will prevent regressions in the migration path.

Proposed test addition
+    `@patch`("dcim.models.Interface", new_callable=MagicMock)
+    `@patch`("dcim.models.Device", new_callable=MagicMock)
+    `@patch`("virtualization.models.VMInterface", new_callable=MagicMock)
+    `@patch`("virtualization.models.VirtualMachine", new_callable=MagicMock)
+    `@patch`("django.contrib.contenttypes.models.ContentType")
+    `@patch`("extras.models.CustomField")
+    def test_migrates_integer_field_to_json(
+        self, MockCustomField, MockContentType, mock_vm, mock_vmif, mock_device, mock_iface
+    ):
+        from netbox_librenms_plugin import _ensure_librenms_id_custom_field
+
+        mock_cf = MagicMock()
+        mock_cf.type = "integer"
+        mock_cf.object_types.values_list.return_value = [1, 2, 3, 4]
+        self._setup_cf_mock(MockCustomField, mock_cf, False)
+
+        mock_ct = MagicMock()
+        mock_ct.pk = 1
+        self._setup_ct_mock(MockContentType, mock_ct)
+
+        _ensure_librenms_id_custom_field(sender=None, using="default")
+
+        mock_cf.save.assert_called_once_with(using="default", update_fields=["type"])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@netbox_librenms_plugin/tests/test_init.py` around lines 89 - 107, Add a test
that covers the integer→JSON migration path in _ensure_librenms_id_custom_field
by creating a MockCustomField instance (mock_cf) with mock_cf.type set to
"integer" and arranging mocks like in existing tests
(MockContentType/MockCustomField); call
_ensure_librenms_id_custom_field(sender=None) and assert that mock_cf.save was
called with the DB alias and update_fields=["type"] (e.g.,
mock_cf.save.assert_called_with(using=alias, update_fields=["type"])) to ensure
the migration branch is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@netbox_librenms_plugin/import_utils/bulk_import.py`:
- Around line 103-112: The required_perms list in bulk_import_devices_shared()
incorrectly includes "virtualization.change_virtualmachine"; remove that entry
and update the surrounding comment so the permission check only requires
"virtualization.add_virtualmachine" (since the import only creates VMs and never
updates them), ensuring the permission enforcement uses the updated
required_perms symbol and does not block device-only imports that lack VM edit
rights.
- Around line 26-50: The cancellation logic in _is_job_cancelled should rely
solely on RQ/Redis flags and not fall back to the database status; update the
function to import get_queue and RQ Job via RQJob.fetch(str(job.job.job_id),
connection=queue.connection) and return rq_job.is_failed or rq_job.is_stopped,
and in the except/Redis-unavailable path do NOT inspect job.job.status or
refresh_from_db—instead return False (i.e., treat as not-cancelled) or re-raise
the connection-related error so database status is never used for cancellation;
ensure references to _is_job_cancelled, get_queue, RQJob.fetch, and
rq_job.is_failed/is_stopped are used to locate the change.

In `@netbox_librenms_plugin/views/imports/actions.py`:
- Line 465: The code currently reads vc_detection_enabled from request.GET which
loses the POSTed checkbox state from BulkImportConfirmView.post(); change the
logic that builds the payload (the "vc_detection_enabled" value) to read and
persist the flag from the POSTed form (use
request.POST.get("enable_vc_detection") or equivalent) when present (falling
back to GET only if POST is absent) so BulkImportDevicesView receives the actual
user selection; update the location where "vc_detection_enabled" is set and any
passing of that value between BulkImportConfirmView.post() and
BulkImportDevicesView to ensure the POSTed value is carried through.

---

Outside diff comments:
In `@netbox_librenms_plugin/tests/test_init.py`:
- Around line 89-107: Add a test that covers the integer→JSON migration path in
_ensure_librenms_id_custom_field by creating a MockCustomField instance
(mock_cf) with mock_cf.type set to "integer" and arranging mocks like in
existing tests (MockContentType/MockCustomField); call
_ensure_librenms_id_custom_field(sender=None) and assert that mock_cf.save was
called with the DB alias and update_fields=["type"] (e.g.,
mock_cf.save.assert_called_with(using=alias, update_fields=["type"])) to ensure
the migration branch is exercised.
🪄 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: 7cae0749-5e90-4f59-9f3f-fe7b4f5252a5

📥 Commits

Reviewing files that changed from the base of the PR and between a07c37e and 6d155a5.

📒 Files selected for processing (17)
  • docs/usage_tips/custom_field.md
  • netbox_librenms_plugin/__init__.py
  • netbox_librenms_plugin/api/views.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/import_utils/vm_operations.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_confirm.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/paginator.html
  • netbox_librenms_plugin/tests/test_coverage_actions.py
  • netbox_librenms_plugin/tests/test_coverage_api2.py
  • netbox_librenms_plugin/tests/test_coverage_base_views.py
  • netbox_librenms_plugin/tests/test_import_utils.py
  • netbox_librenms_plugin/tests/test_init.py
  • netbox_librenms_plugin/views/base/cables_view.py
  • netbox_librenms_plugin/views/imports/actions.py
💤 Files with no reviewable changes (1)
  • netbox_librenms_plugin/jobs.py

Comment thread netbox_librenms_plugin/import_utils/bulk_import.py Outdated
Comment thread netbox_librenms_plugin/import_utils/bulk_import.py
Comment thread netbox_librenms_plugin/views/imports/actions.py Outdated
marcinpsk added a commit that referenced this pull request Mar 31, 2026
- bulk_import: remove change_virtualmachine from required_perms (import
  only creates VMs, never updates them)
- bulk_import: _is_job_cancelled except branch returns False instead of
  checking DB status (Redis unavailable = not cancelled)
- bulk_import: remove now-unused JobStatusChoices import
- actions: read vc_detection_enabled from POST first, GET as fallback,
  with safe empty-string default to avoid AttributeError
- test_init: add test for integer->JSON field migration branch
- test_import_utils: replace DB-fallback cancellation tests with
  RQ-unavailable returns-False test
- test_vm_operations: update cancellation tests to use patched
  _is_job_cancelled instead of removed DB-fallback path

@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 the current code and only fix it if needed.

Inline comments:
In `@netbox_librenms_plugin/import_utils/bulk_import.py`:
- Around line 29-31: The docstring in
netbox_librenms_plugin/import_utils/bulk_import.py incorrectly claims the
function "falls back to the database status if Redis is unavailable"; update the
docstring to state the actual behavior: the function checks RQ/Redis state first
and, on Redis/Redis-py exceptions, returns False immediately (no DB fallback).
Locate the function whose docstring starts with "Checks RQ/Redis state first..."
and edit that text to accurately describe the exception path and return value.

In `@netbox_librenms_plugin/tests/test_vm_operations.py`:
- Around line 603-630: Rename the misleading test function
test_job_status_value_attribute_used_when_present to a name that reflects its
behavior (e.g., test_redis_unavailable_continues_loop or
test_is_job_cancelled_false_processes_all_vms) and update any references; the
test exercises bulk_import_vms with _is_job_cancelled patched to return False to
ensure the loop continues and both VMs are attempted, so ensure the new name
mentions Redis/unavailable or _is_job_cancelled returning False and adjust the
test function definition accordingly.

In `@netbox_librenms_plugin/views/imports/actions.py`:
- Around line 63-111: Replace the duplicated local function
_resolve_naming_preferences with a call to the shared implementation in utils:
import resolve_naming_preferences from netbox_librenms_plugin.utils at the top
of the module, remove the entire local _resolve_naming_preferences definition,
and update any internal references to call resolve_naming_preferences(request)
(preserving its tuple return of (use_sysname, strip_domain)); ensure imports and
any variable names match the utils function signature.
🪄 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: 227a88d9-3fcf-4b04-abdf-37f549d53850

📥 Commits

Reviewing files that changed from the base of the PR and between 6d155a5 and f330749.

📒 Files selected for processing (6)
  • docs/development/testing.md
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/tests/test_import_utils.py
  • netbox_librenms_plugin/tests/test_init.py
  • netbox_librenms_plugin/tests/test_vm_operations.py
  • netbox_librenms_plugin/views/imports/actions.py

Comment thread netbox_librenms_plugin/import_utils/bulk_import.py Outdated
Comment thread netbox_librenms_plugin/tests/test_vm_operations.py Outdated
Comment thread netbox_librenms_plugin/views/imports/actions.py Outdated
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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: 1

♻️ Duplicate comments (1)
netbox_librenms_plugin/views/imports/actions.py (1)

281-282: ⚠️ Potential issue | 🟠 Major

Pass the parsed VC flag into confirm-time validation.

The new vc_detection_enabled value is only computed for the template context. validate_device_for_import() has already run above with its default include_vc_detection=True, so the confirm modal still performs VC lookups and can show stack-specific validation even when the user filtered with VC detection off. validation["_vc_detection_enabled"] should come from the same parsed boolean.

🔧 Suggested fix
         use_sysname, strip_domain = resolve_naming_preferences(request)
+        vc_detection_enabled = (
+            (request.POST.get("enable_vc_detection") or request.GET.get("enable_vc_detection") or "").lower()
+            in ("on", "true", "1")
+        )
 
         devices = []
         errors = []
         seen_ids = set()
         cache_expired_count = 0
@@
             validation = validate_device_for_import(
                 libre_device,
                 import_as_vm=is_vm,
                 api=self.librenms_api,
+                include_vc_detection=vc_detection_enabled,
                 use_sysname=use_sysname,
                 strip_domain=strip_domain,
                 server_key=self.librenms_api.server_key,
             )
@@
-            vc_requested = request.GET.get("enable_vc_detection") == "true"
-            validation["_vc_detection_enabled"] = vc_requested
+            validation["_vc_detection_enabled"] = vc_detection_enabled
@@
         context = {
             "devices": devices,
             "device_count": len(devices),
             "errors": errors,
             "use_sysname": use_sysname,
             "strip_domain": strip_domain,
             "server_key": self.librenms_api.server_key,
-            "vc_detection_enabled": (
-                request.POST.get("enable_vc_detection") or request.GET.get("enable_vc_detection") or ""
-            ).lower()
-            in ("on", "true", "1"),
+            "vc_detection_enabled": vc_detection_enabled,
         }

Also applies to: 316-323, 414-417

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@netbox_librenms_plugin/views/imports/actions.py` around lines 281 - 282, The
confirm-time validation still uses the default include_vc_detection=True instead
of the parsed VC flag; update the confirm-path to use the parsed boolean
(vc_detection_enabled) by passing include_vc_detection=vc_detection_enabled into
validate_device_for_import and by setting validation["_vc_detection_enabled"] =
vc_detection_enabled (the same parsed value you compute for the template
context). Apply the same change in the other confirm-time blocks referenced
(around the validate_device_for_import calls at the 316-323 and 414-417 regions)
so confirm modal validation and template context use the identical
vc_detection_enabled flag.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@netbox_librenms_plugin/import_utils/bulk_import.py`:
- Around line 145-149: The current call to validate_device_for_import sets
include_vc_detection to True when sync_options is None or empty; change it so a
missing vc_detection_enabled flag is treated as off. Update the
include_vc_detection expression in the validate_device_for_import call (and any
similar calls in bulk_import_devices / bulk_import_devices_shared) to only be
True if sync_options exists and explicitly contains a truthy
"vc_detection_enabled" (e.g. include_vc_detection = bool(sync_options and
sync_options.get("vc_detection_enabled", False)) or check for the key presence),
so default/omitted sync_options behaves as disabled.

---

Duplicate comments:
In `@netbox_librenms_plugin/views/imports/actions.py`:
- Around line 281-282: The confirm-time validation still uses the default
include_vc_detection=True instead of the parsed VC flag; update the confirm-path
to use the parsed boolean (vc_detection_enabled) by passing
include_vc_detection=vc_detection_enabled into validate_device_for_import and by
setting validation["_vc_detection_enabled"] = vc_detection_enabled (the same
parsed value you compute for the template context). Apply the same change in the
other confirm-time blocks referenced (around the validate_device_for_import
calls at the 316-323 and 414-417 regions) so confirm modal validation and
template context use the identical vc_detection_enabled flag.
🪄 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: bb63cc65-9a9c-4582-8829-425b20119fb3

📥 Commits

Reviewing files that changed from the base of the PR and between f330749 and ecf124b.

📒 Files selected for processing (5)
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/tests/test_coverage_actions.py
  • netbox_librenms_plugin/tests/test_import_utils.py
  • netbox_librenms_plugin/tests/test_vm_operations.py
  • netbox_librenms_plugin/views/imports/actions.py

Comment thread netbox_librenms_plugin/import_utils/bulk_import.py
@marcinpsk

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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 and others added 7 commits April 1, 2026 14:14
- __init__.py: per-alias execution tracking (_executed_aliases set), startup
  migration for integer->json librenms_id field, use using(db_alias) for DB ops
- api/views.py: job names from FilterDevicesJob/ImportDevicesJob Meta, add
  filterset_class to InterfaceTypeMappingViewSet, catch NoSuchJobError explicitly,
  update LibreNMSPluginPermission docstring, scope sync_job_status to owner
- import_utils/bulk_import.py: extract _is_job_cancelled helper, remove
  vc_detection_enabled param (handled via sync_options), add
  virtualization.change_virtualmachine to required perms, move _safe_disabled
  import from filters module
- import_utils/vm_operations.py: use _is_job_cancelled helper (dedup)
- jobs.py: remove vc_detection_enabled param from ImportDevicesJob
- views/base/cables_view.py: table=None init before conditional, docstring
  cleanup, explicit for-loop instead of list comprehension
- views/imports/actions.py: inline _resolve_naming_preferences, move
  _DEVICE_ONLY_ACTIONS to module level, fix resolve_naming_preferences import
- tests: update test_init for _executed_aliases/using(db_alias), update
  test_coverage_api2 to raise NoSuchJobError, fix patch paths for get_user_pref
- device_operations: skip site/device-type issues for existing devices on re-import
- bulk_import_confirm.html: pass vc_detection_enabled through bulk import form
- device_validation_details.html: fix existing-device condition and VM model name check
- paginator.html: fix table.page.previous/next_page_number (was page.*)
- docs/usage_tips/custom_field.md: remove 'Legacy' label, update 0.4.2+ note
- test_coverage_base_views.py: add test for non-default server_key forwarded to enrich_links_data
- bulk_import: remove change_virtualmachine from required_perms (import
  only creates VMs, never updates them)
- bulk_import: _is_job_cancelled except branch returns False instead of
  checking DB status (Redis unavailable = not cancelled)
- bulk_import: remove now-unused JobStatusChoices import
- actions: read vc_detection_enabled from POST first, GET as fallback,
  with safe empty-string default to avoid AttributeError
- test_init: add test for integer->JSON field migration branch
- test_import_utils: replace DB-fallback cancellation tests with
  RQ-unavailable returns-False test
- test_vm_operations: update cancellation tests to use patched
  _is_job_cancelled instead of removed DB-fallback path
- bulk_import: fix _is_job_cancelled docstring to reflect that
  Redis-unavailable returns False with no DB fallback
- test_vm_operations: rename test_job_status_value_attribute_used_when_present
  to test_is_job_cancelled_false_processes_all_vms
- actions: remove local _resolve_naming_preferences duplicate; import and
  use the shared resolve_naming_preferences from utils instead
- test_coverage_actions, test_import_utils: update patch targets and
  inline imports to reference utils.resolve_naming_preferences and
  utils.get_user_pref (no longer in actions module)
When sync_options is None or missing the key, default to False (off)
rather than True so omitted options don't silently enable VC detection.
@marcinpsk
marcinpsk force-pushed the pr/code-quality-fixes branch from b0e610f to bbfe3ed Compare April 1, 2026 12:14

@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: 4

♻️ Duplicate comments (1)
netbox_librenms_plugin/views/imports/actions.py (1)

324-326: ⚠️ Potential issue | 🟠 Major

Reuse the normalized VC flag for confirm-time validation.

BulkImportConfirmView.post() now computes vc_detection_enabled for the template context, but Line 330 still derives validation["_vc_detection_enabled"] from request.GET only, and the validate_device_for_import(...) call above still omits include_vc_detection. In the common POST path, the confirm modal can therefore render rows without the same VC state that the final import uses.

🔧 Suggested fix
-        use_sysname, strip_domain = resolve_naming_preferences(request)
+        use_sysname, strip_domain = resolve_naming_preferences(request)
+        vc_detection_enabled = (
+            (request.POST.get("enable_vc_detection") or request.GET.get("enable_vc_detection") or "").lower()
+            in ("on", "true", "1")
+        )
@@
             validation = validate_device_for_import(
                 libre_device,
                 import_as_vm=is_vm,
                 api=self.librenms_api,
+                include_vc_detection=vc_detection_enabled,
                 use_sysname=use_sysname,
                 strip_domain=strip_domain,
                 server_key=self.librenms_api.server_key,
             )
@@
-            vc_requested = request.GET.get("enable_vc_detection") == "true"
-            validation["_vc_detection_enabled"] = vc_requested
+            validation["_vc_detection_enabled"] = vc_detection_enabled
@@
-            "vc_detection_enabled": (
-                request.POST.get("enable_vc_detection") or request.GET.get("enable_vc_detection") or ""
-            ).lower()
-            in ("on", "true", "1"),
+            "vc_detection_enabled": vc_detection_enabled,

Also applies to: 414-417

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@netbox_librenms_plugin/views/imports/actions.py` around lines 324 - 326,
BulkImportConfirmView.post computes vc_detection_enabled for the template but
validation["_vc_detection_enabled"] and the validate_device_for_import(...)
calls still read from request.GET only; fix by reusing the normalized
vc_detection_enabled: set validation["_vc_detection_enabled"] =
vc_detection_enabled and pass include_vc_detection=vc_detection_enabled into
each validate_device_for_import(...) call (the earlier call that currently omits
include_vc_detection and the later call around the other confirm-path block that
mirrors lines ~414-417) so the confirm modal uses the same VC detection state as
the final import.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@netbox_librenms_plugin/__init__.py`:
- Around line 107-114: The startup migration only converts cf.type == "integer"
but must handle any legacy non-JSON types; update the conditional in the
migration block that references cf and created to check cf.type != "json"
instead of cf.type == "integer", then call cf.save(using=db_alias,
update_fields=["type"]) and update the
logging.getLogger("netbox_librenms_plugin").info message to indicate a non-JSON
-> json migration (include cf.type if desired before changing it) so any
manually recreated "text" or other legacy types are fixed on post_migrate.

In
`@netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_confirm.html`:
- Line 135: The change listener for the background-job checkbox is currently
guarded by window.__bulkConfirmHandlersAttached, making it fire only once and
breaking after HTMX re-renders the modal; remove that one-shot guard and rebind
the listener every time the fragment is rendered (or use event delegation) so
that `#use-background-job-checkbox` change always updates
`#use-background-job-hidden` (and any related inputs like vc_detection_enabled)
after each HTMX replace; target the DOM elements by their IDs
(`#use-background-job-checkbox`, `#use-background-job-hidden`) and ensure you either
attach the handler unconditionally on render or safely remove and re-add the
listener rather than relying on window.__bulkConfirmHandlersAttached.

In `@netbox_librenms_plugin/tests/test_init.py`:
- Around line 191-209: Update the test_integer_field_migrated_to_json test to
exercise a non-default DB alias: call _ensure_librenms_id_custom_field with
sender=None and a non-default alias string (e.g. "other") so the per-alias guard
(_executed_aliases) path is exercised, mock the ORM lookups to expect
.objects.using(db_alias) and .objects.db_manager(db_alias) calls on
MockCustomField/MockContentType, and assert mock_cf.save was called with
using=db_alias and update_fields=["type"] instead of hard-coding "default";
ensure the test verifies the alias flows through
MockCustomField.objects.using/.db_manager and the save(using=...) call.

In `@netbox_librenms_plugin/tests/test_vm_operations.py`:
- Around line 603-630: Update the test_is_job_cancelled_false_processes_all_vms
test to explicitly verify that _is_job_cancelled was invoked: when patching
netbox_librenms_plugin.import_utils.vm_operations._is_job_cancelled, capture the
patch as a mock (e.g., mock_is_job_cancelled) with return_value=False and after
calling bulk_import_vms assert
mock_is_job_cancelled.assert_called_once_with(mock_job); this ensures the helper
(_is_job_cancelled) is actually consulted during bulk_import_vms execution and
protects against accidental removal of the cancellation check.

---

Duplicate comments:
In `@netbox_librenms_plugin/views/imports/actions.py`:
- Around line 324-326: BulkImportConfirmView.post computes vc_detection_enabled
for the template but validation["_vc_detection_enabled"] and the
validate_device_for_import(...) calls still read from request.GET only; fix by
reusing the normalized vc_detection_enabled: set
validation["_vc_detection_enabled"] = vc_detection_enabled and pass
include_vc_detection=vc_detection_enabled into each
validate_device_for_import(...) call (the earlier call that currently omits
include_vc_detection and the later call around the other confirm-path block that
mirrors lines ~414-417) so the confirm modal uses the same VC detection state as
the final import.
🪄 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: e797966c-f9f4-4d41-9eae-98c32aaa82ac

📥 Commits

Reviewing files that changed from the base of the PR and between b0e610f and 1e4fa5e.

📒 Files selected for processing (20)
  • docs/development/testing.md
  • docs/usage_tips/custom_field.md
  • netbox_librenms_plugin/__init__.py
  • netbox_librenms_plugin/api/views.py
  • netbox_librenms_plugin/import_utils/bulk_import.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/import_utils/vm_operations.py
  • netbox_librenms_plugin/jobs.py
  • netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_sync.js
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/bulk_import_confirm.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/inc/paginator.html
  • netbox_librenms_plugin/tests/test_coverage_actions.py
  • netbox_librenms_plugin/tests/test_coverage_api2.py
  • netbox_librenms_plugin/tests/test_coverage_base_views.py
  • netbox_librenms_plugin/tests/test_import_utils.py
  • netbox_librenms_plugin/tests/test_init.py
  • netbox_librenms_plugin/tests/test_vm_operations.py
  • netbox_librenms_plugin/views/base/cables_view.py
  • netbox_librenms_plugin/views/imports/actions.py
💤 Files with no reviewable changes (1)
  • netbox_librenms_plugin/jobs.py

Comment thread netbox_librenms_plugin/__init__.py
Comment thread netbox_librenms_plugin/tests/test_init.py Outdated
Comment thread netbox_librenms_plugin/tests/test_vm_operations.py
Catch specific Redis/RQ exceptions in _is_job_cancelled and log unexpected
ones, so real bugs aren't masked. Document vc_detection_enabled in
ImportDevicesJob.run sync_options docstring.
- bulk_import_confirm.html: delegate the background-job checkbox change
  listener on document so it keeps working after HTMX re-renders the modal.
- test_init.py: exercise the per-alias path by migrating an integer field
  on a non-default DB alias and assert .using/.db_manager/save use it.
- test_vm_operations.py: capture the _is_job_cancelled patch and assert it
  was invoked, guarding the cancellation check from accidental removal.
- views/imports/actions.py: normalize vc_detection_enabled once and reuse
  it for validate_device_for_import, validation["_vc_detection_enabled"],
  and the template context so the confirm modal matches the final import.
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