feat(api): Enhance session events, plugin objects API with pagination, sorting, and filtering#1576
feat(api): Enhance session events, plugin objects API with pagination, sorting, and filtering#1576
Conversation
…iltering - Added support for pagination (page and limit) in the session events endpoint. - Implemented sorting functionality based on specified columns and directions. - Introduced free-text search capability for session events. - Updated SQL queries to retrieve all events and added a new SQL constant for events. - Refactored GraphQL types and helpers to support new plugin and event queries. - Created new GraphQL resolvers for plugins and events with pagination and filtering. - Added comprehensive tests for new GraphQL endpoints and session events functionality.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR adds server-side pagination, sorting, and filtering for events and plugin tables via new GraphQL types/resolvers and shared helpers, migrates frontend tables to GraphQL-backed DataTables (serverSide), extends the sessions REST endpoint with paging/sort/search, and updates documentation and tests accordingly. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant FE as Frontend (DataTable)
participant API as GraphQL Endpoint
participant Helpers as Pagination/Filters
participant Store as JSON/Data Source
User->>FE: request page/sort/search
FE->>API: POST /graphql with options (page,limit,sort,search)
API->>Store: load table JSON (events/plugins...)
Store-->>API: return raw rows
API->>Helpers: apply_plugin_filters / apply_events_filters
Helpers-->>API: filtered rows
API->>Helpers: apply_common_pagination(filtered, options)
Helpers-->>API: paged entries + counts
API-->>FE: {entries, count, db_count}
FE->>FE: update DataTable display
FE-->>User: show paged results
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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: 2
🧹 Nitpick comments (3)
docs/API_SESSIONS.md (1)
235-237: Add language specifier to fenced code block.The static analysis tool flags a missing language specifier. Consider adding
textor leaving it explicitly blank.📝 Proposed fix
**Example:** - ``` + ```text /sessions/session-events?type=all&period=7 days&page=1&limit=25&sortCol=3&sortDir=desc ```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/API_SESSIONS.md` around lines 235 - 237, Add a language specifier to the fenced code block that contains the example request "/sessions/session-events?type=all&period=7 days&page=1&limit=25&sortCol=3&sortDir=desc" by changing the opening backticks to include a language (e.g., ```text) so the block is explicitly marked as plain text.server/api_server/sessions_endpoint.py (1)
429-455: Sorting is applied before filtering, so ordering may be suboptimal for large result sets.The current flow is: sort all rows → filter → paginate. For server-side DataTables, sorting should typically happen after filtering to avoid sorting rows that will be excluded by the search. While the final results are correct, sorting thousands of rows before discarding most via search is inefficient.
Additionally, note that
total(line 440) represents the count before the free-text search is applied, which aligns with DataTables'recordsTotalsemantics. This is correct.♻️ Suggested reorder: filter then sort
all_rows = table_data["data"] + # --- Free-text search (applied after formatting so display values are searchable) --- + if search: + search_lower = search.strip().lower() + + def _row_matches(r): + return any(search_lower in str(v).lower() for v in r if v is not None) + all_rows = [r for r in all_rows if _row_matches(r)] + + total = len(all_rows) # recordsTotal before pagination + records_filtered = len(all_rows) # same after search in this flow + # --- Sorting --- num_cols = len(all_rows[0]) if all_rows else 0 if 0 <= sort_col < num_cols: reverse = sort_dir.lower() == "desc" all_rows.sort( key=lambda r: (r[sort_col] is None, r[sort_col] if r[sort_col] is not None else ""), reverse=reverse, ) - total = len(all_rows) - - # --- Free-text search (applied after formatting so display values are searchable) --- - if search: - search_lower = search.strip().lower() - - def _row_matches(r): - return any(search_lower in str(v).lower() for v in r if v is not None) - all_rows = [r for r in all_rows if _row_matches(r)] - records_filtered = len(all_rows) - # --- Pagination ---Note: If
totalis meant to represent the count before any search (standard DataTablesrecordsTotal), then keep the current logic but move sorting after filtering for efficiency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/api_server/sessions_endpoint.py` around lines 429 - 455, The current code sorts all_rows before applying the free-text filter, which is inefficient; change the order so filtering happens first (use search and _row_matches to produce filtered_rows), then compute records_filtered = len(filtered_rows), then perform the sorting based on sort_col/sort_dir against filtered_rows, then paginate into paged_rows and return {"data": paged_rows, "total": total, "recordsFiltered": records_filtered}; keep total as the count before filtering (len(all_rows) from the original table_data["data"]) so DataTables' recordsTotal semantics are preserved.test/api_endpoints/test_graphq_endpoints.py (1)
365-368: Case-insensitive MAC comparison may cause false negatives.The assertion uppercases
entry['eveMac']but the comparison value is already uppercase ("00:00:00:00:00:00"). However, if the server returns lowercase MACs, this will still pass. Consider normalizing both sides consistently:- assert entry["eveMac"].upper() == "00:00:00:00:00:00", ( + assert entry["eveMac"].lower() == "00:00:00:00:00:00", (This is a minor robustness improvement since the MAC
00:00:00:00:00:00is already uniform.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/api_endpoints/test_graphq_endpoints.py` around lines 365 - 368, Normalize both sides of the MAC comparison to avoid case-related false negatives: when asserting each entry in result["entries"], convert entry["eveMac"] and the expected string "00:00:00:00:00:00" using the same normalization (e.g., .lower() or .upper()/casefold()) before comparing; update the assertion referencing result, entry, and entry["eveMac"] so both sides use the same normalized form.
🤖 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 366-372: The fkOpt string interpolates foreignKey directly into
the GraphQL fragment (fkOpt, foreignKey, prefixes, fragments,
pluginsObjects/pluginsEvents/pluginsHistory), allowing special characters to
break the query; fix by normalizing/sanitizing foreignKey before building fkOpt
(e.g. restrict to expected MAC chars like [0-9A-Fa-f:.-] or otherwise escape
quotes) and then use that sanitized value when building fkOpt (or wrap the value
via a proper escaping/JSON-encoding routine) so the generated fragments cannot
contain untrusted quote or control characters.
In `@server/api_server/graphql_helpers.py`:
- Around line 48-52: The current slice computation uses options.page directly,
so page values <= 0 produce negative start indices and unexpected slices; update
the pagination logic (the block computing effective_limit, start, end and
slicing data) to validate options.page >= 1 before using it—either clamp page to
1 or raise a clear error when options.page is < 1, and ensure the code uses the
validated page value when computing start = (page - 1) * effective_limit and end
= start + effective_limit to avoid negative slice bounds.
---
Nitpick comments:
In `@docs/API_SESSIONS.md`:
- Around line 235-237: Add a language specifier to the fenced code block that
contains the example request "/sessions/session-events?type=all&period=7
days&page=1&limit=25&sortCol=3&sortDir=desc" by changing the opening backticks
to include a language (e.g., ```text) so the block is explicitly marked as plain
text.
In `@server/api_server/sessions_endpoint.py`:
- Around line 429-455: The current code sorts all_rows before applying the
free-text filter, which is inefficient; change the order so filtering happens
first (use search and _row_matches to produce filtered_rows), then compute
records_filtered = len(filtered_rows), then perform the sorting based on
sort_col/sort_dir against filtered_rows, then paginate into paged_rows and
return {"data": paged_rows, "total": total, "recordsFiltered":
records_filtered}; keep total as the count before filtering (len(all_rows) from
the original table_data["data"]) so DataTables' recordsTotal semantics are
preserved.
In `@test/api_endpoints/test_graphq_endpoints.py`:
- Around line 365-368: Normalize both sides of the MAC comparison to avoid
case-related false negatives: when asserting each entry in result["entries"],
convert entry["eveMac"] and the expected string "00:00:00:00:00:00" using the
same normalization (e.g., .lower() or .upper()/casefold()) before comparing;
update the assertion referencing result, entry, and entry["eveMac"] so both
sides use the same normalized form.
🪄 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: 4d4ca716-090c-4d35-bf07-1053a94b9524
📒 Files selected for processing (15)
CONTRIBUTING.mddocs/API_GRAPHQL.mddocs/API_SESSIONS.mdfront/deviceDetailsEvents.phpfront/events.phpfront/pluginsCore.phpserver/api.pyserver/api_server/api_server_start.pyserver/api_server/graphql_endpoint.pyserver/api_server/graphql_helpers.pyserver/api_server/graphql_types.pyserver/api_server/sessions_endpoint.pyserver/const.pytest/api_endpoints/test_graphq_endpoints.pytest/api_endpoints/test_sessions_endpoints.py
Summary by CodeRabbit
New Features
UI
Documentation