Skip to content

feat(api): Enhance session events, plugin objects API with pagination, sorting, and filtering#1576

Merged
jokob-sk merged 2 commits intomainfrom
next_release
Mar 27, 2026
Merged

feat(api): Enhance session events, plugin objects API with pagination, sorting, and filtering#1576
jokob-sk merged 2 commits intomainfrom
next_release

Conversation

@jokob-sk
Copy link
Copy Markdown
Collaborator

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

  • 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.

Summary by CodeRabbit

  • New Features

    • Server-side pagination, sorting, and searching for plugin tables and events for better performance.
    • Sessions endpoint supports pagination, sorting, and free-text search.
    • New GraphQL endpoints for events and plugin data with advanced filtering.
  • UI

    • Plugin tabs load data on demand and display refreshed badge counts.
    • Events table now uses server-side draws and improved loading/error handling.
  • Documentation

    • Updated API docs with pagination/query specs.
    • CONTRIBUTING updated with AI-assisted usage and commit-message guidance.

…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.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c710e9a-da7e-43b5-9b7f-ddf3437a65e5

📥 Commits

Reviewing files that changed from the base of the PR and between ec3e4c8 and 7305fd7.

📒 Files selected for processing (1)
  • server/api_server/graphql_helpers.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/api_server/graphql_helpers.py

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Documentation
CONTRIBUTING.md, docs/API_GRAPHQL.md, docs/API_SESSIONS.md
Added "Use of AI" contributor guidance, PR/commit guidance, documented new GraphQL top-level queries (events, pluginsObjects, pluginsEvents, pluginsHistory) and /sessions/session-events pagination/sort/search parameters and response shape.
Frontend Migration
front/deviceDetailsEvents.php, front/events.php, front/pluginsCore.php
Replaced client-side SQL/API reads with GraphQL queries; switched DataTables to serverSide: true; added AJAX handlers passing page/limit/sort/search; lazy-init plugin sub-tabs and badge prefetching; moved hide-connections filtering to client-side.
GraphQL Types & Helpers
server/api_server/graphql_types.py, server/api_server/graphql_helpers.py
Introduced centralized graphene input/object types for pagination/sort/filter, result wrappers, plus helpers: sorting key, apply_common_pagination, apply_plugin_filters, apply_events_filters, and pagination limits/defaults.
GraphQL Resolvers
server/api_server/graphql_endpoint.py
Removed inline schema/type definitions, imported new types/helpers, added resolvers for events, pluginsObjects, pluginsEvents, pluginsHistory, and a shared _resolve_plugin_table helper; use file-backed JSON sources with filtering/pagination.
Server REST & Data Exposure
server/const.py, server/api.py, server/api_server/sessions_endpoint.py, server/api_server/api_server_start.py
Added sql_events_all constant and exposed events in dataSources; extended /sessions/session-events handler and get_session_events() signature to accept and enforce page, limit (max 1000), search, sortCol, sortDir, and return {data,total,recordsFiltered}.
Tests
test/api_endpoints/test_graphq_endpoints.py, test/api_endpoints/test_sessions_endpoints.py
Added GraphQL tests for new queries (entries/count/dbCount, pagination caps, filtering/scoping) and REST tests validating /sessions/session-events pagination and sorting behavior.

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
Loading

Possibly related PRs

  • PR #1429: GraphQL endpoint/schema refactoring and field metadata changes that overlap with this PR's migration of types/resolvers.
  • PR #1326: Changes to sessions/events handling and API surface that intersect with the extended sessions endpoint and event exposure here.
  • PR #1560: Overlapping updates to GraphQL/events and frontend event pages (pagination/sorting/camelCase adjustments).

Poem

🐰 I nibble bytes and hop through rows,
GraphQL tunnels where the data flows,
Pages turn, filters dance and gleam,
Badges count like carrot beams,
Hooray — the rabbit loves this schema dream! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective: enhancing session events and plugin objects APIs with pagination, sorting, and filtering capabilities across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% 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 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: 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 text or 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' recordsTotal semantics. 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 total is meant to represent the count before any search (standard DataTables recordsTotal), 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:00 is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 54a249f and ec3e4c8.

📒 Files selected for processing (15)
  • CONTRIBUTING.md
  • docs/API_GRAPHQL.md
  • docs/API_SESSIONS.md
  • front/deviceDetailsEvents.php
  • front/events.php
  • front/pluginsCore.php
  • server/api.py
  • server/api_server/api_server_start.py
  • server/api_server/graphql_endpoint.py
  • server/api_server/graphql_helpers.py
  • server/api_server/graphql_types.py
  • server/api_server/sessions_endpoint.py
  • server/const.py
  • test/api_endpoints/test_graphq_endpoints.py
  • test/api_endpoints/test_sessions_endpoints.py

@jokob-sk jokob-sk merged commit 9b71662 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