feat(plugins): Implement /plugins/stats endpoint for per-plugin row c…#1581
feat(plugins): Implement /plugins/stats endpoint for per-plugin row c…#1581
Conversation
…ounts with optional foreignKey filtering
📝 WalkthroughWalkthroughThe PR introduces a new REST API endpoint Changes
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
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
server/api_server/openapi/schemas.py (1)
1092-1096: Tighten thetableNamecontract.The backend and frontend only support
objects,events, andhistory, 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 onrow.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
foreignKeybranch now does threeWHERE foreignKey = ? GROUP BY pluginscans per request. IfPlugins_Objects,Plugins_Events, andPlugins_Historyare not indexed onforeignKey— 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
📒 Files selected for processing (5)
front/pluginsCore.phpserver/api_server/api_server_start.pyserver/api_server/openapi/schemas.pyserver/models/plugin_object_instance.pytest/api_endpoints/test_plugin_stats_endpoints.py
…ounts with optional foreignKey filtering
Summary by CodeRabbit
New Features
/plugins/statsREST API endpoint to retrieve plugin statistics with optional filtering by device MAC address.Tests