Skip to content

fix(viper): align integrationUpload payload with Viper OpenAPI (#160)#169

Merged
xnorkl merged 4 commits into
developfrom
bug/160-viper-schema
May 21, 2026
Merged

fix(viper): align integrationUpload payload with Viper OpenAPI (#160)#169
xnorkl merged 4 commits into
developfrom
bug/160-viper-schema

Conversation

@t-a-y-l-o-r
Copy link
Copy Markdown
Collaborator

@t-a-y-l-o-r t-a-y-l-o-r commented May 21, 2026

Summary

Fixes #160. Aligns BlueFlow's outbound integrationUpload payload with Viper's actual OpenAPI schema so pages stop coming back as 400.

ViperAsset

  • Add ip (from asset.ip_address) and utilization (projected from Usage rows in the Monday-first sparse {hour: count} shape).
  • Populate upstream_api from BASE_URL + asset.id (was ""); role from asset.category (was "").
  • Status literal: "active""Active" (Viper enum is Active | Decommissioned | Maintenance).
  • Expand location from {} to {facility, building, floor, room} with empty-string defaults (not nullable per Viper docs).
  • Rename vendorIDvendor_id.
  • Drop BlueFlow's PK id from the payload (strict schema rejects extras).
  • Stop pruning cpe/role — both ship as "" when unset.

ViperWebhookResponse

  • Wrapper renames: totaltotal_count, next_page/previous_page properties → next/previous.
  • Internal-only fields (since, before, request_id, webhook_path) are kept on the dataclass for URL generation but stripped from to_dict() output via a new _INTERNAL_KEYS ClassVar.

Tests

  • New blueflow/tests/test_viper_models.py — 21 unit tests on ViperAsset.to_dict() and ViperWebhookResponse.to_dict() covering required keys, dropped extras, status enum, utilization projection, role-from-category mapping, upstream URL pattern, IP fallback, location shape, and all wrapper renames.
  • Updated existing blueflow/celery/tests/test_viper.py assertions to reflect the new wire shape (total_count, next, previous, internal fields stripped).

Drive-by lint/cleanup commits

  • chore(lint): exempt ARG001 in test files — pytest fixtures are declared via the test function's argument list and their value is the side effect, not the return; ruff's "unused argument" rule is a false positive in test contexts. Centralizes the exemption in pyproject.toml and removes 6 now-redundant # noqa: ARG001 pragmas across 4 unrelated test files. ruff-format also reflowed two pre-existing call sites in test_upsert.py.
  • chore(tests): drop redundant db arg from test functions — the autouse _auto_db fixture in the root conftest.py already enables DB access for every test, so declaring db as a function argument is dead weight. Removes the arg from 10 test functions across 4 files. Verified by stash-and-rerun: pre-change and post-change runs of the touched files report the same 26 baseline failures and 3 baseline lints (no new errors introduced).

Decisions worth flagging

  • max_pages=1 × page_size=500 ceiling — Viper's inbound webhook currently sends those values, so any hospital with >500 assets will silently lose the tail. Out of scope here, but worth a follow-up issue or a Viper-side bump.
  • status mapping — hardcoded "Active". BlueFlow has no status concept today (no Asset.status field, no tag convention). When one lands, swap the literal for a mapping; not building a one-branch helper now.
  • location shape — empty strings on all four sub-keys until a BlueFlow source (custom fields? tags?) is decided.
  • Casing converter (pageSize, totalCount, etc.) — not in this PR. The plan is that snake_case will be handled at serialize/requests-lib time. The casing converter needs to be scoped to the outbound view only — applying it globally would silently break the inbound webhook parser, which uses snake_case (max_pages, page_size).

Test plan

  • uv run pytest blueflow/tests/test_viper_models.py blueflow/celery/tests/test_viper.py blueflow/views/tests/test_viper.py blueflow/tests/test_asset_usage.py → 43/43 green on a fresh DB
  • Stash-and-rerun comparison on the 4 files touched by the db sweep — same 26 baseline failures and 3 baseline lints before/after; no regressions
  • just boot && just capture && just integrate against demo-0.3.1 with the init/blueflow-patches/tasks.py overlay unmounted — no 400 from Viper, just check viper matches just check blueflow
  • Confirm casing converter strategy (separate PR) before merging into demo-0.3.2

Closes #160.

Summary by CodeRabbit

  • Bug Fixes

    • Viper webhook payloads no longer leak internal request fields.
    • Asset optional fields (cpe, role) are now always present with proper defaults.
  • New Features

    • Asset utilization data now included in Viper webhook payloads.
    • Improved pagination fields in webhook responses (total_count, next, previous).
  • Tests

    • Added comprehensive test suite for Viper integration models.
    • Refactored test fixtures across multiple test modules.

Review Change Stack

ViperAsset now emits the keys Viper actually requires:
- ip from asset.ip_address (was missing)
- upstream_api from BASE_URL + asset.id (was "")
- role from asset.category (was "")
- vendor_id snake_case (was vendorID)
- utilization projected from Usage rows (was missing)
- status "Active" (was "active")
- location {facility, building, floor, room} (was {})
- BlueFlow PK id dropped (extras rejected by strict schema)
- cpe/role always present as "" (no longer pruned)

ViperWebhookResponse aligns wrapper keys:
- total -> total_count
- next_page/previous_page -> next/previous
- since, before, request_id, webhook_path stripped from wire
  payload (still on the dataclass for URL generation)

Adds blueflow/tests/test_viper_models.py with 21 unit tests
covering the dataclass to_dict() outputs end-to-end.
Pytest declares fixtures via the test function's argument list; the body
never reads them by name (their value is the side effect, not the return).
Ruff's "unused argument" check is a false positive in test contexts and
was forcing per-line noqa pragmas. Centralize the exemption in
pyproject.toml and let ruff strip the now-redundant noqas. ruff-format
also reflowed two pre-existing call sites in test_upsert.py.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

Restructures Viper dataclasses and webhook serialization (utilization projection, JSON-safe fields, status casing), renames pagination/wrapper keys to total_count/items/next/previous, strips internal-only fields from payloads, adds unit tests, updates integration tests, and relaxes Ruff test ignores for ARG001.

Changes

Viper webhook schema alignment

Layer / File(s) Summary
ViperAsset and ViperWebhookResponse schema changes
blueflow/models/viper.py
_project_usage() generates Monday-first utilization. ViperAsset builds JSON-safe string identity fields, includes status, cpe, role, and utilization, and to_dict() returns the dataclass mapping. ViperWebhookResponse introduces total_count, _INTERNAL_KEYS, emits items (not assets), and exposes next/previous URLs instead of next_page/previous_page.
ViperAsset and ViperWebhookResponse schema validation tests
blueflow/tests/test_viper_models.py
New unit tests validate per-item serialization (required keys, no internal id, status capitalization, role/cpe fallbacks, upstream_api formation, IP/location rules, utilization shape and zero-count omission) and response wrapper renames (total_count, items, next, previous) plus stripping of internal-only fields.
Celery webhook integration test updates
blueflow/celery/tests/test_viper.py
Updates integration assertions to use total_count, next/previous semantics (nullable on boundaries), ensure internal request fields are not POSTed, add test_viper_asset_optional_fields_always_present, and adjust regression checks to total_count.
Ruff configuration and test annotation cleanup
pyproject.toml, blueflow/tests/*, tests/*
Adds ARG001 to Ruff per-file-ignores for test patterns and removes inline # noqa: ARG001 from multiple test functions; minor payload formatting changes preserved test behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • virtalabs/blueflow#155: Adds utilization transformation and Usage model alignment referenced by the _project_usage changes.
  • virtalabs/blueflow#168: Overlaps on ViperWebhookResponse serialization and ISO since/before handling used in pagination URL generation.

Suggested labels

api

Suggested reviewers

  • xnorkl

Poem

🐰 I hopped through dataclasses late at night,
Made camel-case shine and upstream links right,
Seven-day hours aligned in a row,
Stripped internal crumbs so webhooks can go,
Now Viper and BlueFlow snugly unite.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: aligning the Viper integration payload with the OpenAPI specification. It directly reflects the primary objective of fixing issue #160.
Linked Issues check ✅ Passed The PR fully implements all coding-related requirements from issue #160: payload structure alignment, field renaming, status casing, required field inclusion, extra field removal, wrapper key renames, and test coverage.
Out of Scope Changes check ✅ Passed All changes directly support the #160 objectives or are justifiable drive-by cleanups. The test fixture updates, linting pragmas, and unused parameter removals are scope-appropriate improvements.
Docstring Coverage ✅ Passed Docstring coverage is 91.11% which is sufficient. The required threshold is 80.00%.

✏️ 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 bug/160-viper-schema

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.

@t-a-y-l-o-r t-a-y-l-o-r self-assigned this May 21, 2026
@t-a-y-l-o-r t-a-y-l-o-r marked this pull request as ready for review May 21, 2026 17:24
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
blueflow/models/viper.py (1)

197-208: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prefetch usage rows before building ViperAsset items.

ViperAsset.__init__ calls _project_usage(asset), which touches asset.usage.all() per asset. Without prefetching, large pages incur N+1 DB queries.

Proposed fix
-        assets = assets.order_by("modified").all()
+        assets = assets.prefetch_related("usage").order_by("modified").all()
🤖 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 `@blueflow/models/viper.py` around lines 197 - 208, The loop causes N+1 queries
because ViperAsset.__init__ calls _project_usage which accesses
asset.usage.all(); fix by prefetching those related rows on the queryset before
materializing/chunking it (e.g. call prefetch_related('usage') or a Prefetch for
the usage relation on the Asset queryset referenced in the assets variable) so
the usage rows are loaded in bulk prior to constructing ViperAsset instances.
🤖 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 `@blueflow/tests/test_csv_byte_order_mark.py`:
- Line 47: The tests referencing the missing fixture setup_db (e.g.,
test_process_csv_without_bom) fail because setup_db doesn't exist; replace the
fixture usage with pytest-django's built-in db fixture or annotate the module
with `@pytest.mark.django_db` so the tests can access the database; update calls
to use db instead of setup_db (or add the module-level marker) for both tests
that currently pass setup_db.

In `@blueflow/tests/test_csv_custom_field.py`:
- Line 12: Replace the unavailable fixture usage in the test function
test_process_csv_with_custom_field (and the other tests referencing setup_db) by
using the available Django DB fixture: change the parameter setup_db to db or
decorate the test with `@pytest.mark.django_db` so the ORM is accessible; update
the function signature(s) (e.g., def test_process_csv_with_custom_field(db):)
and remove references to setup_db within those tests to ensure CI uses the
provided db fixture.

---

Outside diff comments:
In `@blueflow/models/viper.py`:
- Around line 197-208: The loop causes N+1 queries because ViperAsset.__init__
calls _project_usage which accesses asset.usage.all(); fix by prefetching those
related rows on the queryset before materializing/chunking it (e.g. call
prefetch_related('usage') or a Prefetch for the usage relation on the Asset
queryset referenced in the assets variable) so the usage rows are loaded in bulk
prior to constructing ViperAsset instances.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33f354d6-c62f-4fa8-a5be-a7a0c9528177

📥 Commits

Reviewing files that changed from the base of the PR and between 1db4ac0 and cc706e4.

📒 Files selected for processing (8)
  • blueflow/celery/tests/test_viper.py
  • blueflow/models/viper.py
  • blueflow/tests/test_asset_hostname.py
  • blueflow/tests/test_csv_byte_order_mark.py
  • blueflow/tests/test_csv_custom_field.py
  • blueflow/tests/test_upsert.py
  • blueflow/tests/test_viper_models.py
  • pyproject.toml

Comment thread blueflow/tests/test_csv_byte_order_mark.py
Comment thread blueflow/tests/test_csv_custom_field.py
The autouse `_auto_db` fixture in the root conftest.py already enables
database access for every test, so declaring `db` as a function argument
is dead weight. Removes the arg from 10 test functions across 4 files.

Pre-change and post-change runs of the touched files report the same
26 baseline failures and 3 baseline lints — no new errors introduced.
@t-a-y-l-o-r
Copy link
Copy Markdown
Collaborator Author

Screenshot 2026-05-21 at 10 38 47 Screenshot 2026-05-21 at 10 43 32 Screenshot 2026-05-21 at 10 44 01 Screenshot 2026-05-21 at 10 44 21 Screenshot 2026-05-21 at 10 45 10 Screenshot 2026-05-21 at 10 47 31

@xnorkl xnorkl self-requested a review May 21, 2026 19:20
Comment thread blueflow/models/viper.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@tests/test_models.py`:
- Around line 4-7: The tests contain function-local imports (e.g., "from
blueflow.models import Asset" inside test_asset_model and similar import(s) in
the other test around lines 12-15) which trigger PLC0415; move these imports to
module scope at the top of tests/test_models.py so both test_asset_model and the
other test(s) import their dependencies from blueflow.models (e.g., Asset and
any other model names referenced) once at file scope rather than inside the test
functions.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: dfc02ae6-88bc-4bc7-8c88-dcfaf14626e9

📥 Commits

Reviewing files that changed from the base of the PR and between cc706e4 and b917e4b.

📒 Files selected for processing (5)
  • blueflow/models/viper.py
  • blueflow/tests/test_asset.py
  • blueflow/tests/test_asset_hostname.py
  • blueflow/tests/test_upsert.py
  • tests/test_models.py

Comment thread tests/test_models.py
Comment on lines +4 to 7
def test_asset_model():
"""Smoke test: Asset can be created."""
from blueflow.models import Asset

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move function-local imports to module scope to fix PLC0415.

Line 6 and Line 14 violate the active lint rule (PLC0415), and CI is already failing on this file.

💡 Proposed fix
 """Tests for blueflow models (mirrors blueflow/models)."""
 
+from blueflow.models import Asset, Tag
+
 
 def test_asset_model():
     """Smoke test: Asset can be created."""
-    from blueflow.models import Asset
 
     asset = Asset.objects.create()
     assert asset.pk is not None
     assert Asset.objects.filter(pk=asset.pk).exists()
 
 def test_tag_model():
     """Smoke test: Tag model exists and has expected attributes."""
-    from blueflow.models import Tag
 
     assert hasattr(Tag, "objects")

Also applies to: 12-15

🧰 Tools
🪛 GitHub Actions: Lint / lint

[error] 6-6: PLC0415 import should be at the top-level of a file

🤖 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 `@tests/test_models.py` around lines 4 - 7, The tests contain function-local
imports (e.g., "from blueflow.models import Asset" inside test_asset_model and
similar import(s) in the other test around lines 12-15) which trigger PLC0415;
move these imports to module scope at the top of tests/test_models.py so both
test_asset_model and the other test(s) import their dependencies from
blueflow.models (e.g., Asset and any other model names referenced) once at file
scope rather than inside the test functions.

@xnorkl xnorkl self-requested a review May 21, 2026 20:16
Copy link
Copy Markdown
Contributor

@xnorkl xnorkl left a comment

Choose a reason for hiding this comment

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

Even better solution than the proposed.

@xnorkl xnorkl merged commit 7c01696 into develop May 21, 2026
3 checks passed
@t-a-y-l-o-r t-a-y-l-o-r deleted the bug/160-viper-schema branch May 22, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] ViperWebhookResponse / ViperAsset payload does not match Viper's integrationUpload OpenAPI

2 participants