UN-3770 [FEAT] Opt-in pagination for list endpoints; wire Workflows page#2187
Conversation
Four list endpoints (workflows, prompt studio, adapters, connectors)
returned every row as a bare array and filtered client-side, making the
pages slow to load as orgs grow.
Backend: add `OptionalPagination` — it paginates only when the caller sends
`?page`/`?page_size`, otherwise returns None so DRF serialises the bare
array. These endpoints are shared with dropdown/selector consumers that
expect an array, so the opt-in keeps their responses byte-identical while
the listing page opts in and receives the {count,next,previous,results}
envelope. Attach it to the four viewsets and add server-side name search;
Workflow also gets a deterministic order (id tiebreaker) for stable pages.
Frontend: the Workflows page now paginates + searches server-side, reusing
the existing `usePaginatedList` hook and the Pipelines wiring pattern. This
is the first vertical slice — the prompt-studio, adapters and connectors
pages will follow (their backends already ship the dormant pagination).
Test: utils/tests/test_pagination.py pins the opt-in contract (bare array
without a page param; envelope with one).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
Summary by CodeRabbit
WalkthroughThe change adds opt-in pagination and server-side name search to backend list endpoints, then updates the workflow frontend to request, render, and refresh paginated results while preserving bare-array responses when pagination is not requested. ChangesPagination and search flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Workflows
participant usePaginatedList
participant workflowService
participant WorkflowViewSet
Workflows->>usePaginatedList: request page, page size, and search
usePaginatedList->>workflowService: getProjectList(params)
workflowService->>WorkflowViewSet: GET workflow list
WorkflowViewSet-->>workflowService: filtered results and pagination metadata
workflowService-->>Workflows: update project list and pagination state
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
SonarCloud python:S5332 flags the clear-text http:// scheme in the request-stub build_absolute_uri; the scheme is inert in this DB-free contract test but tripping the B security rating on the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
|
| Filename | Overview |
|---|---|
| backend/utils/pagination.py | Adds optional pagination that preserves bare-array responses when no pagination parameter is provided. |
| backend/utils/tests/test_pagination.py | Adds contract tests for the opt-in pagination behavior and blank pagination parameters. |
| backend/workflow_manager/workflow_v2/views.py | Adds optional pagination, server-side workflow search, and deterministic workflow ordering. |
| backend/adapter_processor_v2/views.py | Adds optional pagination, adapter name search, and deterministic adapter ordering. |
| backend/connector_v2/views.py | Adds optional pagination, connector name search, and deterministic connector ordering. |
| backend/prompt_studio/prompt_studio_core_v2/views.py | Adds optional pagination, prompt studio search, and deterministic prompt studio ordering. |
| frontend/src/components/workflows/workflow/Workflows.jsx | Updates the Workflows page to fetch paginated results, run server-side search, and refresh the active page after mutations. |
| frontend/src/components/workflows/workflow/workflow-service.js | Updates the workflow list API helper to send pagination and search parameters. |
| frontend/src/components/workflows/workflow/Workflows.css | Adds layout styling for the Workflows pagination control. |
Reviews (4): Last reviewed commit: "UN-3770 [FIX] Lock blank-pagination-para..." | Re-trigger Greptile
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
frontend/src/components/workflows/workflow/Workflows.jsx (1)
2-2: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove unused
Typographyimport.The
Typographycomponent fromantdis imported but never used in this file.♻️ Proposed refactor
-import { Pagination, Typography } from "antd"; +import { Pagination } from "antd";🤖 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 `@frontend/src/components/workflows/workflow/Workflows.jsx` at line 2, Remove the unused Typography import from the antd import declaration in Workflows.jsx, while retaining the Pagination import.frontend/src/components/workflows/workflow/workflow-service.js (1)
27-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDeclare
optionsto prevent global variable leakage.The
optionsobject is assigned without a variable declaration, which creates an implicit global variable. While this appears to be a pre-existing pattern in this service file, it is best practice to declare variables locally.♻️ Proposed refactor
getProjectList: (params = {}) => { - options = { + const options = { url: `${path}/workflow/`, method: "GET", params, }; return axiosPrivate(options); },🤖 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 `@frontend/src/components/workflows/workflow/workflow-service.js` around lines 27 - 34, Declare options locally within getProjectList before assigning the request configuration, preventing implicit global leakage while preserving the existing axiosPrivate call and request parameters.
🤖 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 `@backend/adapter_processor_v2/views.py`:
- Around line 193-196: Ensure stable pagination ordering after the for_user()
queryset filtering: in backend/adapter_processor_v2/views.py lines 193-196,
backend/connector_v2/views.py lines 108-112, and
backend/prompt_studio/prompt_studio_core_v2/views.py lines 177-180, apply an
explicit descending modified_at ordering with ascending id as the unique
tiebreaker before returning each queryset (including qs in the prompt studio
view).
In `@frontend/src/components/workflows/workflow/Workflows.jsx`:
- Around line 155-163: Remove the unconditional handleListRefresh() call after
setAlertDetails in the workflow update success handler, preserving the existing
refresh inside the editingProject?.name branch while retaining the refresh
behavior for non-edit updates.
- Around line 139-141: Update the getProjectList failure handler in the workflow
component to set projectList to an empty array before logging the error,
ensuring the existing projectList-based rendering shows the empty state rather
than an indefinite SpinnerLoader after the initial request fails.
- Around line 128-137: Update the paginated workflow fetch handling around the
results assignment and setProjectList call so an empty results array on a page
greater than the first automatically refetches or navigates to the previous page
before updating the displayed list. Keep page 1 empty behavior unchanged, and
ensure pagination state reflects the fallback page so controls remain usable.
---
Nitpick comments:
In `@frontend/src/components/workflows/workflow/workflow-service.js`:
- Around line 27-34: Declare options locally within getProjectList before
assigning the request configuration, preventing implicit global leakage while
preserving the existing axiosPrivate call and request parameters.
In `@frontend/src/components/workflows/workflow/Workflows.jsx`:
- Line 2: Remove the unused Typography import from the antd import declaration
in Workflows.jsx, while retaining the Pagination import.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8c025dc8-c7d4-4a4e-b448-90c184af9b36
📒 Files selected for processing (9)
backend/adapter_processor_v2/views.pybackend/connector_v2/views.pybackend/prompt_studio/prompt_studio_core_v2/views.pybackend/utils/pagination.pybackend/utils/tests/test_pagination.pybackend/workflow_manager/workflow_v2/views.pyfrontend/src/components/workflows/workflow/Workflows.cssfrontend/src/components/workflows/workflow/Workflows.jsxfrontend/src/components/workflows/workflow/workflow-service.js
…pty-page fallback - OptionalPagination: treat blank ?page=/?page_size= as not opting in so shared callers keep their bare-array response (Greptile). - adapter/connector/prompt-studio get_queryset: order by the DISTINCT ON field for deterministic pagination, incl. the admin/service branch that has no distinct; not modified_at (conflicts with DISTINCT ON) (CodeRabbit). - Workflows.jsx: step back a page when a delete empties the current page (CodeRabbit). - Drop redundant "server-side name search" WHAT comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
…tch failure - Drop the unconditional handleListRefresh() after edit; the branch already refreshes existing edits and navigates for new ones (CodeRabbit). - Default projectList to [] on fetch failure so the empty state shows instead of an indefinite spinner (CodeRabbit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/workflows/workflow/Workflows.jsx (1)
148-150: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winPrevent early clearance of loading state during chained requests.
Using an unconditional
.finally()block to clear the sharedloadingstate inadvertently hides the spinner when a chained operation has already initiated a new request. This causes the UI to temporarily freeze while the second network call is in flight.
frontend/src/components/workflows/workflow/Workflows.jsx#L148-L150: IngetProjectList,finallyclears the loading state even when falling back to the previous page (getProjectList(page - 1)). Remove.finally()and explicitly callsetLoading(false)inside.then()(after state updates, skipping the fallback return) and.catch().frontend/src/components/workflows/workflow/Workflows.jsx#L179-L181: IneditProject,finallyclears the loading state immediately afterhandleListRefresh()triggers a new list fetch. Remove.finally()and let the list refresh manage the loading state, while explicitly clearing it in theopenProjectand.catch()paths.🤖 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 `@frontend/src/components/workflows/workflow/Workflows.jsx` around lines 148 - 150, The loading state is cleared prematurely by unconditional finally blocks during chained requests. In frontend/src/components/workflows/workflow/Workflows.jsx lines 148-150, update getProjectList to remove finally and call setLoading(false) in then after state updates, except before returning for the previous-page fallback, and in catch. In lines 179-181, update editProject to remove finally, let handleListRefresh manage loading after refresh, and explicitly clear loading in the openProject and catch paths.
🤖 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.
Outside diff comments:
In `@frontend/src/components/workflows/workflow/Workflows.jsx`:
- Around line 148-150: The loading state is cleared prematurely by unconditional
finally blocks during chained requests. In
frontend/src/components/workflows/workflow/Workflows.jsx lines 148-150, update
getProjectList to remove finally and call setLoading(false) in then after state
updates, except before returning for the previous-page fallback, and in catch.
In lines 179-181, update editProject to remove finally, let handleListRefresh
manage loading after refresh, and explicitly clear loading in the openProject
and catch paths.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7153dbac-8d3d-4cc9-ab52-3e1ac91d512a
📒 Files selected for processing (7)
backend/adapter_processor_v2/views.pybackend/connector_v2/views.pybackend/prompt_studio/prompt_studio_core_v2/views.pybackend/utils/pagination.pybackend/utils/tests/test_pagination.pybackend/workflow_manager/workflow_v2/views.pyfrontend/src/components/workflows/workflow/Workflows.jsx
💤 Files with no reviewable changes (1)
- backend/workflow_manager/workflow_v2/views.py
🚧 Files skipped from review as they are similar to previous changes (5)
- backend/utils/pagination.py
- backend/utils/tests/test_pagination.py
- backend/prompt_studio/prompt_studio_core_v2/views.py
- backend/adapter_processor_v2/views.py
- backend/connector_v2/views.py
…e size
Cover the mixed blank/non-blank pagination params. A blank ?page= alongside a
real ?page_size= serves the first page rather than raising NotFound, since DRF
resolves the page number as `query_params.get("page") or 1`.
Also drop the Workflows page size from 12 to 10 to match the other paginated
listing pages, which use the usePaginatedList default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
Unstract test resultsPer-group results
Critical paths
|



What & why
Jira: UN-3770
Four listing endpoints — workflows, prompt studio, adapters, connectors — return every row as a bare array and filter client-side, so the pages get slow to load as an org grows (noticeably Prompt Studio projects and Workflows). This adds opt-in server-side pagination.
The regression constraint
These endpoints are shared. Besides the listing page they feed dropdowns/selectors that expect a bare array:
/adapter?adapter_type=…→CombinedOutput,OutputForDocModal,AdapterSelectionModal,AddLlmProfile,DefaultTriadconnector/→ConfigureDsworkflow/list →EtlTaskDeployAttaching a DRF
pagination_classunconditionally would turn all their responses into the{count,next,previous,results}envelope and break every one of those consumers.Approach
OptionalPagination(utils/pagination.py): paginates only when the request carries?page/?page_size, otherwise returnsNone→ DRF serialises the bare array. Only the listing page opts in.?search=icontains).WorkflowViewSetgets a deterministic order (-modified_at, id) — its manager uses plain.distinct()with no default order, which pagination needs for stable pages. The other three already order byDISTINCT ON (id/tool_id).usePaginatedListhook and the Pipelines wiring pattern. Bare-array fallback (data.results ?? data) kept.Scope / follow-ups
This is the first vertical slice. The prompt-studio, adapters and connectors frontend pages are intentionally not converted yet — their backends already ship the (dormant) pagination, and each page has intricate search/refresh flows that warrant live validation one at a time under the same ticket.
Testing
backend/utils/tests/test_pagination.pypins the opt-in contract (bare array without a page param; envelope with one). Verified standalone (backend test tier needs the full env).ruff+ frontendbiomeclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z