Skip to content

fix(environments): let the environments list take a page_size - #8913

Open
TejasLamba2006 wants to merge 4 commits into
Flagsmith:mainfrom
TejasLamba2006:fix/environments-page-size
Open

TejasLamba2006 wants to merge 4 commits into
Flagsmith:mainfrom
TejasLamba2006:fix/environments-page-size

Conversation

@TejasLamba2006

Copy link
Copy Markdown

Problem

EnvironmentViewSet had no pagination_class, so it fell back to DRF's global default of 10 results per page and ignored the page_size query parameter. PR #8654 worked around this on the frontend with a fetchAllPages / getAllEnvironments helper.

Changes

Backend (api/environments/views.py)

  • Set pagination_class = CustomPagination on EnvironmentViewSet, matching the pattern used by features, identities, and experiments viewsets.

Frontend

  • Remove the getAllEnvironments RTK endpoint (which used fetchAllPages to paper over the missing server-side pagination).
  • Switch useGroupedBreakdown to useGetEnvironmentsQuery and read .results from the standard paged response.
  • Remove the now-unused getAllEnvironments request and response types.

Tests (api/tests/unit/environments/test_unit_environments_views.py)

  • test_list_environments__returns_paginated_response: verifies the list action returns a paginated envelope (results, count).
  • test_list_environments__page_size_param_is_respected: verifies page_size controls result count.

Notes

CustomPagination defaults to page_size=999 and max_page_size=999, so existing callers that do not pass page_size get up to 999 environments per page - well above any realistic project size, so there is no regression for callers that relied on the old unpaginated response shape.

EnvironmentViewSet had no pagination_class, so it fell back to DRF's
default of 10 per page and ignored the page_size query parameter.

- Set pagination_class = CustomPagination on EnvironmentViewSet so
  callers can pass page_size and receive a standard paginated response
- Drop the getAllEnvironments RTK endpoint (which used fetchAllPages to
  work around the missing pagination) in favour of getEnvironments
- Update useGroupedBreakdown to use useGetEnvironmentsQuery and read
  .results from the paged response
- Remove the now-unused getAllEnvironments request/response types
- Add backend tests covering the paginated response shape and that
  page_size is respected
@TejasLamba2006
TejasLamba2006 requested review from a team as code owners November 9, 2026 16:30
@TejasLamba2006
TejasLamba2006 requested review from khvn26 and kyle-ssg and removed request for a team November 9, 2026 16:30
@vercel

vercel Bot commented Nov 9, 2026

Copy link
Copy Markdown

@TejasLamba2006 is attempting to deploy a commit to the Flagsmith Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Explain Change →

Walkthrough

The environment list endpoint now uses CustomPagination. Tests cover project filtering, result counts, page sizes, page links and the maximum page size. The frontend removes the all-environments query and its request and response types. The usage breakdown now uses the project-scoped environments query and reads environment names from its results array.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: Low · up to 7c850

The pagination change is mergeable with bounded follow-up: projects with more than 999 permitted environments can show existing environments as “Deleted environments” in usage breakdowns. Fetching all pages would correct those labels.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: c37ab70b-d4cc-4ee4-85b1-6eb39fdea3ec
Commits

Reviewing files that changed from the base of the PR and between 13539af and d5e8676.

Files selected for processing: 6

  • api/environments/views.py
  • api/tests/unit/environments/test_unit_environments_views.py
  • frontend/common/services/useEnvironment.ts
  • frontend/common/types/requests.ts
  • frontend/common/types/responses.ts
  • frontend/web/components/pages/usage/components/UsageBreakdown/useGroupedBreakdown.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment on lines +53 to 54
const environments = useGetEnvironmentsQuery(
groupBy === 'environment' && projectId ? { projectId } : skipToken,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Low value

Supported by static analysis

Script executed:

rg -n 'function byScope|const byScope|export .*byScope|fetchAllPages|useGetAllEnvironmentsQuery|useGetEnvironmentsQuery|group_by.*environment' frontend/web/components/pages/usage frontend/common/services frontend/common/utils | head -100

Repository: Flagsmith/flagsmith

Length of output: 1411


Script executed:

git diff 13539afcb99c20b4351e26f215d01b49f2a9be03 d5e8676c5717fcf26498f7c4c77b2226beef31df -- frontend/web/components/pages/usage/components/UsageBreakdown/useGroupedBreakdown.ts frontend/common/services/useEnvironment.ts api/environments/views.py
nl -ba frontend/web/components/pages/usage/components/UsageBreakdown/useGroupedBreakdown.ts
nl -ba frontend/web/components/pages/usage/components/UsageBreakdown/utils.ts | sed -n '1,220p'
nl -ba frontend/common/services/useEnvironment.ts | sed -n '1,140p'
nl -ba frontend/common/utils/fetchAllPages.ts
rg -n -i 'organisation.?usage|organization.?usage|group_by|groupBy' frontend/web/components/pages/usage frontend/common/services api/organisations api/usage 2>/dev/null

Repository: Flagsmith/flagsmith

Length of output: 25426


Script executed:

nl -ba frontend/common/services/useOrganisationUsage.ts | sed -n '1,80p'
nl -ba api/app/pagination.py
nl -ba api/environments/views.py | sed -n '80,220p'
rg -n -i -e 'group_by' -e 'environment_id' -- api | grep -i -E 'usage|analytics|influx|event' 
rg -n -i -e 'environment.limit' -e 'max.environments' -e 'environments.limit' -e 'maximum.number.of.environments' -- api frontend | grep -v -E 'lock|snapshot'

Repository: Flagsmith/flagsmith

Length of output: 48186


Script executed:

rg -n -F -e 'get_usage_data' -e 'usage-data' -- api/app_analytics api/organisations
rg -n -F -e 'class CreateEnvironmentSerializer' -e 'class Environment(' -- api/environments
nl -ba api/app_analytics/analytics_db_service.py | sed -n '35,190p'
nl -ba api/app_analytics/analytics_db_service.py | sed -n '355,400p'
nl -ba api/tests/unit/app_analytics/test_analytics_db_service.py | sed -n '960,1125p'
nl -ba api/tests/unit/app_analytics/test_unit_app_analytics_views.py | sed -n '460,492p'
rg -n -i -e 'environments_per' -e 'environment_count' -e 'environment_limit' -e 'environments_limit' -e 'max_environments' -- api | grep -v -E 'migrations|tests/unit/integrations' || test "$?" -eq 1

Repository: Flagsmith/flagsmith

Length of output: 19773


Script executed:

nl -ba api/app_analytics/views.py | sed -n '1,135p'
nl -ba api/environments/serializers.py | sed -n '1,200p'
nl -ba api/environments/models.py | sed -n '75,180p'
nl -ba frontend/web/components/pages/usage/components/UsageBreakdown/UsageBreakdown.tsx
nl -ba frontend/web/components/pages/usage/components/UsageBreakdown/breakdownView.ts | sed -n '1,120p'

Repository: Flagsmith/flagsmith

Length of output: 22769


Fetch every environment page for the usage breakdown.

When a project has more than 999 permitted environments, usage for an environment on a later page can appear under Deleted environments. Fetch all environment pages before building the name lookup.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: e9c14bf0-14a9-4f59-95fd-7b9a74839683
Commits

Reviewing files that changed from the base of the PR and between d5e8676 and 42c1b39.

Files selected for processing: 1

  • api/tests/unit/environments/test_unit_environments_views.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: eb99fec5-05ea-4e4f-a59a-b3e220ee00d1
Commits

Reviewing files that changed from the base of the PR and between 42c1b39 and c08c154.

Files selected for processing: 1

  • api/tests/unit/environments/test_unit_environments_views.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 1 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: c03f6e3f-0e3b-41bf-9f71-aaa0f231966b
Commits

Reviewing files that changed from the base of the PR and between c08c154 and 7c850da.

Files selected for processing: 1

  • api/tests/unit/environments/test_unit_environments_views.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 0 remain after this review.

Comment on lines +1424 to +1425
assert len(data["results"]) == 11
assert data["next"] is None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial · Maintainability & Code Quality · Low value

Supported by static analysis

Script executed:

sed -n '1390,1520p' api/tests/unit/environments/test_unit_environments_views.py
cat api/app/pagination.py

Repository: Flagsmith/flagsmith

Length of output: 7849


Script executed:

rg -n -F -e 'CustomPagination' -e 'test_list_environments__' -e 'page_size' api/environments/views.py api/tests/unit/environments/test_unit_environments_views.py api/tests/unit/app/ api/tests/unit/ 2>/dev/null | grep -E 'environments/views.py|environments/test_unit_environments_views.py|pagination' 
nl -ba api/tests/unit/environments/test_unit_environments_views.py | sed -n '1400,1510p'
nl -ba api/app/pagination.py | sed -n '1,22p'

Repository: Flagsmith/flagsmith

Length of output: 12255


Script executed:

rg -n -F -e 'CustomPagination' -e 'test_list_environments__' -e 'page_size' api/environments/views.py api/tests/unit/environments/test_unit_environments_views.py api/tests/unit/app/ api/tests/unit/ 2>/dev/null | grep -E 'environments/views.py|environments/test_unit_environments_views.py|pagination'
nl -ba api/tests/unit/environments/test_unit_environments_views.py | sed -n '1400,1510p'
nl -ba api/app/pagination.py | sed -n '1,22p'

Repository: Flagsmith/flagsmith

Length of output: 12255


Cover the 999-result default boundary.

The current test detects the former 10-result default, but not a change from 999 to 11. Consider a separate test with 1,000 environments that checks for 999 results and a next page.

environments = [
Environment.objects.create(name=f"Env {i}", project=project) for i in range(5)
]
mocker.patch.object(CustomPagination, "max_page_size", 3)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial · Functional Correctness · Low value

Supported by static analysis

Script executed:

sed -n '1430,1520p' api/tests/unit/environments/test_unit_environments_views.py
rg -n 'max_page_size|10000|999' api/tests/unit/app api/tests/unit/environments api/app/pagination.py

Repository: Flagsmith/flagsmith

Length of output: 5542


Script executed:

printf '%s\n' '--- Pagination and environment list test ---'
nl -ba api/app/pagination.py | sed -n '1,40p'
nl -ba api/environments/views.py | sed -n '55,115p'
nl -ba api/tests/unit/environments/test_unit_environments_views.py | sed -n '1385,1508p'
printf '%s\n' '--- Environment model location ---'
rg -n 'class Environment\(|def save\(|post_save|pre_save|Environment\.objects\.bulk_create' api/environments api/tests/unit/environments --glob '*.py' | sed -n '1,110p'

Repository: Flagsmith/flagsmith

Length of output: 10404


Script executed:

printf '%s\n' '--- Environment model and creation hooks ---'
nl -ba api/environments/models.py | sed -n '65,230p'
rg -n -F -e 'sender=Environment' -e 'sender = Environment' -e 'post_save.connect' -e 'pre_save.connect' api/environments api/features api/projects --glob '*.py' || test "$?" -eq 1
printf '%s\n' '--- Edge identity limit test ---'
nl -ba api/tests/integration/edge_api/identities/test_edge_identity_viewset.py | sed -n '220,265p'

Repository: Flagsmith/flagsmith

Length of output: 11432


Test the configured page-size limit.

The patched maximum tests capping but does not protect the configured limit of 999. Add an unpatched test with more than 999 environments and page_size=10000. Assert that the response contains 999 results and a next page.

page_2_ids = {environment["id"] for environment in page_2_data["results"]}
page_3_ids = {environment["id"] for environment in page_3_data["results"]}
assert len(page_2_data["results"]) == 2
assert page_2_data["next"] is not None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial · Functional Correctness · Low value

Supported by static analysis

Script executed:

sed -n '1430,1520p' api/tests/unit/environments/test_unit_environments_views.py
rg -n 'next.*]|next.*link|page_2_data' api/tests/unit/environments

Repository: Flagsmith/flagsmith

Length of output: 4379


Script executed:

rg -n -F -e 'CustomPagination' -e '["next"]' -e "['next']" api/tests/unit/environments api/tests/integration 2>/dev/null; printf '\n--- identity pagination test ---\n'; nl -ba api/tests/unit/environments/identities/test_unit_identities_views.py | sed -n '255,320p'; printf '\n--- pagination implementation ---\n'; nl -ba api/app/pagination.py | sed -n '1,100p'; printf '\n--- endpoint ---\n'; nl -ba api/environments/views.py | sed -n '65,135p'; printf '\n--- changed test ---\n'; nl -ba api/tests/unit/environments/test_unit_environments_views.py | sed -n '1400,1515p'

Repository: Flagsmith/flagsmith

Length of output: 15262


Follow the page-two link in the test.

The test requests page three independently, so it accepts a next link that omits parameters or points back to page two. Follow page_2_data["next"] to check that the link returns the final environment.

Suggested change

-    page_3_response = admin_client_new.get(
-        url,
-        data={"project": project.id, "page_size": 2, "page": 3},
-        content_type="application/json",
-    )
+    page_3_response = admin_client_new.get(page_2_response.json()["next"])

This branch has not been deployed

No deployments
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