Skip to content

feat(plugins): Implement /plugins/stats endpoint for per-plugin row c…#1581

Merged
jokob-sk merged 1 commit intomainfrom
next_release
Mar 27, 2026
Merged

feat(plugins): Implement /plugins/stats endpoint for per-plugin row c…#1581
jokob-sk merged 1 commit intomainfrom
next_release

Conversation

@jokob-sk
Copy link
Copy Markdown
Collaborator

@jokob-sk jokob-sk commented Mar 27, 2026

…ounts with optional foreignKey filtering

Summary by CodeRabbit

  • New Features

    • Added a new /plugins/stats REST API endpoint to retrieve plugin statistics with optional filtering by device MAC address.
    • Optimized plugin statistics loading with pre-computed data for enhanced performance.
  • Tests

    • Added comprehensive test coverage for the new plugin statistics API endpoint, including authorization, data structure validation, and filtering scenarios.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

The PR introduces a new REST API endpoint /plugins/stats that returns aggregated plugin statistics, and refactors the frontend's fetchPluginCounts() to use pre-computed JSON files for unfiltered requests and the new endpoint for MAC-filtered requests.

Changes

Cohort / File(s) Summary
API Endpoint Implementation
server/api_server/api_server_start.py, server/api_server/openapi/schemas.py, server/models/plugin_object_instance.py
Added authenticated GET /plugins/stats route with Pydantic schemas (PluginStatsEntry, PluginStatsResponse). Backend method getStats() aggregates row counts from plugin tables (Plugins_Objects, Plugins_Events, Plugins_History) with optional foreignKey filtering via UNION ALL query.
Frontend Integration
front/pluginsCore.php
Refactored fetchPluginCounts() to load pre-computed stats from table_plugins_stats.json for unfiltered requests; replaced GraphQL batch POST with GET call to /plugins/stats?foreignKey=... for MAC-filtered requests. Single post-processing loop derives counts from returned rows.
Testing
test/api_endpoints/test_plugin_stats_endpoints.py
Added four test cases covering unauthorized access (HTTP 403), successful response validation (HTTP 200 with success: True), entry structure validation (tableName, plugin, cnt fields), and filtering by foreignKey parameter.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Frontend<br/>(pluginsCore.php)
    participant API as API Server<br/>(api_server_start.py)
    participant DB as Database<br/>(plugin_object_instance.py)
    
    alt MAC-Unfiltered Request
        Client->>Client: Load pre-computed<br/>table_plugins_stats.json
        Client->>Client: Parse stats.data
    else MAC-Filtered Request
        Client->>API: GET /plugins/stats?foreignKey=XX:XX:XX
        API->>DB: getStats(foreign_key)
        DB->>DB: Execute UNION ALL query<br/>across plugin tables
        DB-->>API: rows [{tableName,<br/>plugin, cnt}]
        API-->>Client: {success: True, data: rows}
    end
    
    Client->>Client: Derive counts by<br/>mapping tableName<br/>to {objects, events, history}
    Client->>Client: Return counts or null
Loading

Possibly related PRs

Poem

🐰 Stats cascade through pipelines clean,
Pre-computed JSON, the fastest seen,
GraphQL batches fade to night,
REST endpoints shine so bright,
Plugin counts now hop with glee! 🐇

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main feature being implemented: a new /plugins/stats endpoint for per-plugin row counts with filtering capabilities.

✏️ 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 next_release

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.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (3)
server/api_server/openapi/schemas.py (1)

1092-1096: Tighten the tableName contract.

The backend and frontend only support objects, events, and history, but this schema currently advertises any string. Narrowing the field here keeps generated clients aligned with the real API and helps catch casing/name drift before the UI indexes on row.tableName.

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

In `@server/api_server/openapi/schemas.py` around lines 1092 - 1096, The
PluginStatsEntry.schema exposes tableName as any string; tighten it to only
allow the three supported values to keep clients and validation correct. Modify
PluginStatsEntry to use a constrained literal/enum for tableName (e.g., replace
tableName: str with a Literal['objects','events','history'] or a small Enum used
as the field type) and add the necessary import (typing.Literal or enum.Enum) so
Pydantic/OpenAPI generation only accepts those three values; keep the existing
description text.
test/api_endpoints/test_plugin_stats_endpoints.py (1)

36-72: Seed known plugin rows before asserting the happy path.

These tests all pass when data == [], and the loop at Line 51 becomes vacuous on an empty DB. A broken handler that always returns an empty list can still pass here. Insert fixture rows into the plugin tables and assert exact unfiltered vs. foreignKey-filtered counts.

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

In `@test/api_endpoints/test_plugin_stats_endpoints.py` around lines 36 - 72, The
tests (test_plugin_stats_success, test_plugin_stats_entry_structure,
test_plugin_stats_with_foreignkey) are vacuously passing when the plugins stats
endpoint returns an empty list; seed deterministic plugin rows before exercising
the endpoint and assert concrete expected results. Update the tests to create
fixture records (e.g., insert known rows into the plugin tables or use a
provided DB fixture) that include multiple tableName values and at least one row
with foreignKey "00:00:00:00:00:00", call GET "/plugins/stats" and assert the
total unfiltered count equals the number of seeded rows, then call GET
"/plugins/stats?foreignKey=00:00:00:00:00:00" and assert the filtered count
equals the expected subset; keep the existing structure checks (tableName,
plugin, cnt) but replace loose existence assertions with exact count comparisons
to detect regressions.
server/models/plugin_object_instance.py (1)

109-129: Check index coverage for the new filtered aggregate.

The foreignKey branch now does three WHERE foreignKey = ? GROUP BY plugin scans per request. If Plugins_Objects, Plugins_Events, and Plugins_History are not indexed on foreignKey — ideally (foreignKey, plugin) — MAC-filtered plugin pages will get slower as those tables grow.

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

In `@server/models/plugin_object_instance.py` around lines 109 - 129, The filtered
aggregate runs three WHERE foreignKey = ? GROUP BY plugin queries and will be
slow without proper indexes; add composite indexes on (foreignKey, plugin) for
Plugins_Objects, Plugins_Events, and Plugins_History (e.g. create indexes named
like idx_plugins_objects_foreignkey_plugin,
idx_plugins_events_foreignkey_plugin, idx_plugins_history_foreignkey_plugin) via
a DB migration or ALTER INDEX/CREATE INDEX IF NOT EXISTS so the query in the
function using self._fetchall benefits from index seeks and covers the GROUP BY
plugin.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@front/pluginsCore.php`:
- Around line 375-377: The fast-path uses the cached snapshot (fetchJson ->
stats -> rows) as a hard visibility filter so generateTabs still uses
pluginCounts to decide tab existence and hides plugins added after the snapshot;
change generateTabs (or the code that filters tabs) to treat snapshot counts as
placeholders only: when a plugin’s snapshot count is zero, perform a live count
check via the existing GraphQL count function (or the existing pluginCounts live
lookup) before excluding the tab, or alternatively only use stats.rows to
populate badge numbers while deriving tab visibility from the live pluginCounts
lookup; update references to fetchJson, stats, rows, and generateTabs
accordingly so zero-counts trigger a live re-check rather than hiding the tab
outright.

---

Nitpick comments:
In `@server/api_server/openapi/schemas.py`:
- Around line 1092-1096: The PluginStatsEntry.schema exposes tableName as any
string; tighten it to only allow the three supported values to keep clients and
validation correct. Modify PluginStatsEntry to use a constrained literal/enum
for tableName (e.g., replace tableName: str with a
Literal['objects','events','history'] or a small Enum used as the field type)
and add the necessary import (typing.Literal or enum.Enum) so Pydantic/OpenAPI
generation only accepts those three values; keep the existing description text.

In `@server/models/plugin_object_instance.py`:
- Around line 109-129: The filtered aggregate runs three WHERE foreignKey = ?
GROUP BY plugin queries and will be slow without proper indexes; add composite
indexes on (foreignKey, plugin) for Plugins_Objects, Plugins_Events, and
Plugins_History (e.g. create indexes named like
idx_plugins_objects_foreignkey_plugin, idx_plugins_events_foreignkey_plugin,
idx_plugins_history_foreignkey_plugin) via a DB migration or ALTER INDEX/CREATE
INDEX IF NOT EXISTS so the query in the function using self._fetchall benefits
from index seeks and covers the GROUP BY plugin.

In `@test/api_endpoints/test_plugin_stats_endpoints.py`:
- Around line 36-72: The tests (test_plugin_stats_success,
test_plugin_stats_entry_structure, test_plugin_stats_with_foreignkey) are
vacuously passing when the plugins stats endpoint returns an empty list; seed
deterministic plugin rows before exercising the endpoint and assert concrete
expected results. Update the tests to create fixture records (e.g., insert known
rows into the plugin tables or use a provided DB fixture) that include multiple
tableName values and at least one row with foreignKey "00:00:00:00:00:00", call
GET "/plugins/stats" and assert the total unfiltered count equals the number of
seeded rows, then call GET "/plugins/stats?foreignKey=00:00:00:00:00:00" and
assert the filtered count equals the expected subset; keep the existing
structure checks (tableName, plugin, cnt) but replace loose existence assertions
with exact count comparisons to detect regressions.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 029c4b69-49b9-4b58-9c21-2eb6bdd0f89d

📥 Commits

Reviewing files that changed from the base of the PR and between 82dafbb and 3f80d2e.

📒 Files selected for processing (5)
  • front/pluginsCore.php
  • server/api_server/api_server_start.py
  • server/api_server/openapi/schemas.py
  • server/models/plugin_object_instance.py
  • test/api_endpoints/test_plugin_stats_endpoints.py

@jokob-sk jokob-sk merged commit d17256c into main Mar 27, 2026
6 checks passed
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