Skip to content

feat(ui): filter by provider group across main views#11659

Open
pfe-nazaries wants to merge 13 commits into
masterfrom
feature/add-provider-group-filter
Open

feat(ui): filter by provider group across main views#11659
pfe-nazaries wants to merge 13 commits into
masterfrom
feature/add-provider-group-filter

Conversation

@pfe-nazaries

@pfe-nazaries pfe-nazaries commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Context

Provider groups let users organize cloud accounts into logical groups. The main views could already be scoped by provider type and provider account, but not by provider group. This PR adds a provider group filter so data can be scoped to the accounts that belong to one or more groups.

Grabacion.de.pantalla.2026-06-22.a.las.9.24.07.mov
Grabacion.de.pantalla.2026-06-22.a.las.9.27.01.mov
Grabacion.de.pantalla.2026-06-22.a.las.9.30.07.mov
Grabacion.de.pantalla.2026-06-22.a.las.9.30.57.mov
Grabacion.de.pantalla.2026-06-22.a.las.9.31.50.mov

Description

  • New ProviderGroupSelector filter control, backed by a manage-groups Server Action that lists the tenant's provider groups
  • Provider group filter wired into the filter bars of the Overview, Findings, Resources, Scans, and Providers views
  • Per-view filter helpers translate the selected groups into the provider-scope query params sent to the API
  • FilterParam type split into core fields plus per-view types
  • Unit tests for the selector, the manage-groups action, and the per-view filter helpers

Steps to review

With at least one provider group defined (containing some accounts), open each page and use the new Provider group filter:

  1. Overview — /
  2. Findings — /findings
  3. Resources — /resources
  4. Scans — /scans
  5. Providers — /providers

Confirm the data is scoped to the accounts in the selected group(s) and that combining it with the existing provider type / account filters behaves as expected.

Checklist

Community Checklist
  • This feature/issue is listed in here or roadmap.prowler.com
  • Is it assigned to me, if not, request it via the issue/feature in here or Prowler Community Slack

UI (if applicable)

  • All issue/task requirements work as expected on the UI
  • Screenshots/Video - Mobile (X < 640px)
  • Screenshots/Video - Tablet (640px > X < 1024px)
  • Screenshots/Video - Desktop (X > 1024px)
  • Ensure new entries are added to ui/CHANGELOG.md

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added provider group filtering across the Overview, Findings, Resources, Scans, and Providers views via a new provider group selector.
  • Improvements
    • Updated risk charts and drill-down flows to respect selected provider groups, including preserving provider-group filters when navigating from severity trends.
    • Filter chips now display provider-group names (when available) instead of raw IDs.

Pablo F.G and others added 7 commits June 22, 2026 08:51
- Add getAllProviderGroups to fetch every group for filter dropdowns
- Add reusable ProviderGroupSelector with batch and instant modes
- Add provider group chip/label resolution and the provider_groups filter param

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add the provider group selector to the Findings filters

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add the provider group selector to the Resources filters

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add the provider group selector to the Overview filters
- Scope risk plot, risk pipeline, and severity-over-time to the selected group

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add the provider group selector to the Scan Jobs filter bar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add the provider group selector to the Providers page filters
- Mock getAllProviderGroups in the providers page data tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add FILTER_FIELD field-name source and a generic FilterParam<Field> template
- Replace the FilterType enum with the same const dictionary
- Add per-view filter param types co-located with each view's action
- Tighten findings/resources filter maps and the findings display fn to their params

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pfe-nazaries pfe-nazaries requested a review from a team as a code owner June 22, 2026 06:54
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a1e45ea3-0f36-4148-b1e4-494bbe1637bc

📥 Commits

Reviewing files that changed from the base of the PR and between f08d7c9 and d90e444.

📒 Files selected for processing (11)
  • ui/actions/manage-groups/manage-groups.test.ts
  • ui/actions/manage-groups/manage-groups.ts
  • ui/app/(prowler)/_overview/_lib/provider-scope.test.ts
  • ui/app/(prowler)/_overview/_lib/provider-scope.ts
  • ui/app/(prowler)/_overview/graphs-tabs/risk-pipeline-view/risk-pipeline-view.ssr.tsx
  • ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot.ssr.tsx
  • ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx
  • ui/components/findings/findings-filters.utils.ts
  • ui/components/resources/resources-filters.utils.ts
  • ui/lib/helper-filters.test.ts
  • ui/lib/helper-filters.ts

📝 Walkthrough

Walkthrough

Replaces the FilterType enum with a FILTER_FIELD const and generic FilterParam template-literal type, adds per-view filter param modules, introduces getAllProviderGroups for paginated group fetching and provider scope utilities for multi-dimension filtering, creates a ProviderGroupSelector component, extends filter display utilities for group chips, wires provider groups into all five page-level views, and scopes overview graphs to selected provider groups.

Changes

Provider Group Filter Feature

Layer / File(s) Summary
FilterType → FILTER_FIELD refactor and view-specific filter param modules
ui/types/filters.ts, ui/hooks/use-related-filters.ts, ui/hooks/use-filter-batch.test.ts, ui/components/filters/data-filters.ts, ui/actions/findings/findings-filters.ts, ui/actions/overview/overview-filters.ts, ui/actions/providers/providers-filters.ts, ui/actions/resources/resources-filters.ts, ui/actions/scans/scans-filters.ts, ui/actions/finding-groups/finding-groups.ts
Removes FilterType enum and replaces it with FILTER_FIELD const and a generic FilterParam<Field> template-literal type. Cascades the change through useRelatedFilters, the filter-batch test, and data-filters.ts. Adds per-view typed filter param modules (FindingsFilterParam, OverviewFilterParam, ProvidersFilterParam, ResourcesFilterParam, ScansFilterParam) and updates finding-groups.ts to use FindingsFilterParam.
Provider scope utilities
ui/app/(prowler)/_overview/_lib/provider-scope.ts, ui/app/(prowler)/_overview/_lib/provider-scope.test.ts
Introduces parseFilterIds, scopeProvidersByGroup, and filterProvidersByScope helpers that normalize filter inputs, scope providers by group membership, and apply multi-dimension provider filtering (ID, type, group) with AND semantics. Includes unit tests covering ID parsing, group scoping, and all filter dimension combinations.
getAllProviderGroups action and data model
ui/actions/manage-groups/manage-groups.ts, ui/actions/manage-groups/manage-groups.test.ts, ui/types/providers-table.ts
Adds getAllProviderGroups, a paginated fetcher for /provider-groups that accumulates all pages up to a 50-page safety cap and returns a single merged ProviderGroupsResponse with normalized pagination metadata. Adds a Vitest test suite covering multi-page merge, single-page short-circuit, empty first page, fetch error, and error payload on a later page. Adds providerGroups: ProviderGroup[] to ProvidersAccountsViewData.
ProviderGroupSelector component
ui/components/filters/provider-group-selector.tsx, ui/components/filters/provider-group-selector.test.tsx
New client component supporting instant (URL-driven) and batch (controlled) modes. Instant mode reads/writes filter[provider_groups__in] via navigateWithParams; batch mode calls onBatchChange. Includes "Select All" option with ARIA attributes, dynamic trigger label (single name, count, or placeholder), and conditional option rendering. Test suite covers empty state, search/keyword propagation, select-all behavior, trigger text variants, and URL navigation.
Filter display utilities for provider groups
ui/lib/helper-filters.ts, ui/lib/helper-filters.test.ts, ui/components/findings/findings-filters.utils.ts, ui/components/findings/findings-filters.utils.test.ts, ui/components/resources/resources-filters.utils.ts, ui/components/resources/resources-filters.utils.test.ts
Adds getProviderGroupDisplayValue helper that resolves a group ID to its display name. Extends findings and resources filter utils to handle filter[provider_groups__in], retypes FILTER_KEY_LABELS to their view-specific FilterParam union, adds "Provider Group" label entry, and extends chip-building functions with optional providerGroups parameter. New test files verify resolved and fallback display values and chip rendering.
Page-level data fetching, scoping, and prop wiring
ui/app/(prowler)/findings/page.tsx, ui/app/(prowler)/page.tsx, ui/app/(prowler)/providers/page.tsx, ui/app/(prowler)/providers/providers-page.utils.ts, ui/app/(prowler)/providers/providers-page.utils.test.ts, ui/app/(prowler)/resources/page.tsx, ui/app/(prowler)/scans/page.tsx, ui/CHANGELOG.md
Each server page adds getAllProviderGroups() to its Promise.all/allSettled and passes the result into the relevant filter component. providers-page.utils.ts fixes provider-type filter key remapping via PROVIDERS_FILTER_PARAM and returns providerGroups in ProvidersAccountsViewData. Scans page derives typed filter key constants from SCANS_PROVIDER_FILTER_FIELD and expands filterProvidersForPendingRows to check group membership.
Filter UI components rendering ProviderGroupSelector
ui/components/findings/findings-filters.tsx, ui/components/providers/providers-accounts-view.tsx, ui/components/providers/providers-filters.tsx, ui/components/providers/providers-filters.test.tsx, ui/components/resources/resources-filters.tsx, ui/components/scans/scans-filter-bar.tsx, ui/components/scans/scans-filter-bar.test.tsx, ui/components/scans/scans-page-shell.tsx
FindingsFilters, ProvidersFilters, ProvidersAccountsView, ResourcesFilters, ScansFilterBar, and ScansPageShell each gain an optional providerGroups prop (defaulting to []), render ProviderGroupSelector in their filter layout (conditionally for findings when undefined), and forward providerGroups into chip-building calls. Component test files add vi.mock stubs for ProviderGroupSelector.
Overview graphs scoped to selected provider groups
ui/app/(prowler)/_overview/graphs-tabs/risk-pipeline-view/risk-pipeline-view.ssr.tsx, ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot.ssr.tsx, ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx
RiskPipelineViewSSR uses OVERVIEW_FILTER_PARAM to extract group filters, derives scopedProviders from relationships.provider_groups via scopeProvidersByGroup, and uses it instead of allProviders for both account-to-type mapping and provider-type enumeration. RiskPlotSSR consolidates provider filtering into a single filterProvidersByScope call combining ID, type, and group filters with AND semantics. FindingSeverityOverTime extracts and preserves filter[provider_groups__in] in drill-down links and trend fetches.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant ServerPage as Server Page<br/>(findings/resources/scans/<br/>providers/home)
  participant getAllProviderGroups
  participant API as /provider-groups API
  participant FilterComponent as Filter Component<br/>(FindingsFilters, etc.)
  participant ProviderGroupSelector

  Browser->>ServerPage: Request with URL search params
  ServerPage->>getAllProviderGroups: call()
  loop pages 1..N (max 50)
    getAllProviderGroups->>API: GET /provider-groups?page[number]=N&page[size]=100
    API-->>getAllProviderGroups: ProviderGroupsResponse page N
  end
  getAllProviderGroups-->>ServerPage: merged ProviderGroupsResponse
  ServerPage->>FilterComponent: render with providerGroups={groups}
  FilterComponent->>ProviderGroupSelector: render with groups={providerGroups}
  Browser->>ProviderGroupSelector: selects group(s)
  ProviderGroupSelector->>Browser: navigateWithParams sets filter[provider_groups__in]=id1,id2
  Browser->>ServerPage: Request with filter[provider_groups__in]=id1,id2
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • prowler-cloud/prowler#11573: Adds provider_groups/provider_groups__in filter support in backend API filtersets and views, which is the server-side counterpart to this PR's filter[provider_groups__in] URL parameter wiring across all UI views.

Suggested reviewers

  • alejandrobailo
  • pedrooot
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(ui): filter by provider group across main views' clearly and concisely describes the main change: adding provider group filtering functionality across multiple UI views.
Description check ✅ Passed The PR description includes context, detailed implementation summary, review steps, and addresses most checklist items, though some UI/screenshot requirements are incomplete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feature/add-provider-group-filter

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

✅ All necessary CHANGELOG.md files have been updated.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Conflict Markers Resolved

All conflict markers have been successfully resolved in this pull request.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🔒 Container Security Scan

Image: prowler-ui:b041eb1
Last scan: 2026-06-24 11:31:57 UTC

✅ No Vulnerabilities Detected

The container image passed all security checks. No known CVEs were found.

📋 Resources:

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot.ssr.tsx (1)

31-47: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply provider filters cumulatively, not as mutually-exclusive branches.

The else if chain causes active filters to be ignored (e.g., provider_groups__in present skips provider_type__in). This can return incorrect scoped data when multiple filters are set.

Suggested fix
-  if (providerIdFilter) {
+  if (providerIdFilter) {
     // Filter by specific provider IDs
     const selectedIds = String(providerIdFilter)
       .split(",")
       .map((id) => id.trim());
-    filteredProviders = allProviders.filter((p) => selectedIds.includes(p.id));
-  } else if (providerGroupsFilter) {
+    filteredProviders = filteredProviders.filter((p) =>
+      selectedIds.includes(p.id),
+    );
+  }
+
+  if (providerGroupsFilter) {
     // Filter by provider group membership
     const selectedGroupIds = String(providerGroupsFilter)
       .split(",")
       .map((id) => id.trim());
-    filteredProviders = allProviders.filter((p) =>
+    filteredProviders = filteredProviders.filter((p) =>
       p.relationships.provider_groups?.data?.some((group) =>
         selectedGroupIds.includes(group.id),
       ),
     );
-  } else if (providerTypeFilter) {
+  }
+
+  if (providerTypeFilter) {
     // Filter by provider types
     const selectedTypes = String(providerTypeFilter)
       .split(",")
       .map((t) => t.trim().toLowerCase());
-    filteredProviders = allProviders.filter((p) =>
+    filteredProviders = filteredProviders.filter((p) =>
       selectedTypes.includes(p.attributes.provider.toLowerCase()),
     );
   }

As per coding guidelines, "URL/filter stability: Ensure filter changes synchronize with URL state and that query params are preserved appropriately across navigation/drilldowns."

🤖 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 `@ui/app/`(prowler)/_overview/graphs-tabs/risk-plot/risk-plot.ssr.tsx around
lines 31 - 47, The filtering logic in this section uses an else if chain, which
prevents multiple filters from being applied simultaneously. When
providerIdFilter, providerGroupsFilter, and providerTypeFilter are all present,
only the first matching condition executes, ignoring other active filters. Fix
this by converting the else if statements to independent if statements so that
each filter is applied cumulatively to the filteredProviders array. Start with
all providers and progressively apply each filter condition that is present,
ensuring that when multiple filters are active in the URL parameters, they all
contribute to narrowing the results rather than mutually excluding each other.

Source: Coding guidelines

🤖 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 `@ui/actions/findings/findings-filters.ts`:
- Around line 24-31: The findings filter type definition uses inline string
literals in a union type for fields like delta__in, scan, scan_id, scan_id__in,
inserted_at, inserted_at__gte, inserted_at__lte, and muted. Create a const
object with these string values as keys and their corresponding string values,
apply the as const assertion to it, then derive the union type from typeof
objectName[keyof typeof objectName] instead of using the inline union. This
aligns with the repository's typing conventions and improves maintainability.

In `@ui/actions/manage-groups/manage-groups.test.ts`:
- Around line 65-113: Add a new test case in the manage-groups.test.ts file to
verify handling of error payloads on subsequent pages. The test should mock
handleApiResponseMock to resolve successfully for the first page (using
makePage) and then resolve to an error payload object (with error and status
fields) for the second page call. Assert that getAllProviderGroups() returns
undefined in this scenario to prevent silent truncation of data. This covers the
critical regression path where pagination fails mid-fetch after successfully
retrieving initial pages.

In `@ui/actions/manage-groups/manage-groups.ts`:
- Around line 77-84: The pagination logic in the handleApiResponse block for
fetching provider groups does not properly distinguish between legitimate empty
responses and API error responses. When a later page returns an API error
object, it is incorrectly treated as "no more pages" causing partial group data
to be returned. Add a check to detect if the data response contains error
information before treating it as an empty page result, and throw or break the
loop when an error is encountered instead of continuing. This check should be
performed on the ProviderGroupsResponse object in the condition that currently
only checks for !data?.data or empty data arrays.

In `@ui/actions/overview/overview-filters.ts`:
- Around line 7-9: The OverviewFilterParam type uses an inline union type
pattern which violates the coding guidelines. Replace the inline union
"PROVIDER_TYPE" | "PROVIDER_ID" | "PROVIDER_GROUPS" with the required
const-object pattern by creating a const object (e.g., PROVIDER_FIELD_KEYS)
containing these provider field names as keys with `as const`, then modify the
OverviewFilterParam type to derive from keyof typeof of that const object
instead of using the inline union directly.

In `@ui/actions/providers/providers-filters.ts`:
- Around line 9-12: The ProvidersFilterParam type definition currently violates
the mandatory TypeScript type-definition rule by using inline union composition.
Refactor ProvidersFilterParam to follow the const-object pattern established
elsewhere in the codebase by creating a const object that combines the
FILTER_FIELD and PROVIDERS_PAGE_FILTER selections, marking it with `as const`,
and then deriving the type using `typeof` and `keyof typeof` pattern (similar to
how ProvidersPageFilter is defined in types/providers-table.ts). This
consolidates the filter field references into a single const definition and
improves type maintainability.

In `@ui/actions/resources/resources-filters.ts`:
- Around line 7-16: The ResourcesFilterParam type uses inline union syntax
instead of the project's const-derived union convention. Create a const field
map object containing all the filter parameter options (the PROVIDER_TYPE,
PROVIDER_ID, PROVIDER_GROUPS, REGION, SERVICE fields from FILTER_FIELD plus the
"type__in" and "groups__in" string literals), mark it with `as const`, and then
redefine ResourcesFilterParam as FilterParam with a type derived from that const
object using the typeof keyof pattern rather than the current inline union
approach.

In `@ui/actions/scans/scans-filters.ts`:
- Around line 19-24: The ScansFilterParam type uses inline union literals
("state__in", "trigger") which violates the const-object pattern guideline.
Create a dedicated const object (e.g., SCANS_FILTER_FIELD) that contains all the
filter field options as keys, then refactor ScansFilterParam to derive the union
type from this const object using the pattern const X = { ... } as const; type T
= typeof X[keyof typeof X], eliminating the inline union types in the
FilterParam generic.

In `@ui/app/`(prowler)/_overview/_components/provider-group-selector.tsx:
- Around line 60-173: The ProviderGroupSelector component is currently located
in a route-local folder (ui/app/(prowler)/_overview/_components) but is being
reused across multiple features, which violates the coding guideline that
components used in 2 or more places should be moved to a shared location. Move
the entire ProviderGroupSelector component file to a shared module path such as
ui/components/filters/ and then update all import statements throughout the
codebase that reference the old path to point to the new shared location. This
ensures clean dependency direction and avoids cross-feature coupling to overview
internals.

In `@ui/app/`(prowler)/scans/page.tsx:
- Around line 165-170: The Promise.all call with getAllProviders() and
getAllProviderGroups() will cause the entire page to fail if either promise
rejects, making it brittle to transient errors. Replace Promise.all with
Promise.allSettled to handle both fulfilled and rejected states gracefully. Then
update the destructuring of providersData and providerGroupsData to check the
status property of each result and extract the data only when the promise was
fulfilled, defaulting to empty arrays when either promise is rejected.
- Around line 41-51: The pending-row provider filtering currently only includes
PROVIDER_UID_FILTER_KEYS and PROVIDER_TYPE_FILTER_KEYS constants, but is missing
provider-group filter criteria. Add a new constant array
PROVIDER_GROUP_FILTER_KEYS (following the same pattern as the existing UID and
TYPE filter key arrays) that includes the provider-group-related filter keys
from SCANS_PROVIDER_FILTER_FIELD, likely PROVIDER_GROUPS_IN and PROVIDER_GROUPS
fields. This will ensure pending rows use the same provider filtering logic as
backend-filtered scan rows.

In `@ui/components/scans/scans-filter-bar.tsx`:
- Line 3: The ProviderGroupSelector component is currently imported from a
feature-local _overview path, but since it is used across multiple views, it
violates the coding guidelines that require components used in 2+ places to be
moved to shared locations. Move the ProviderGroupSelector component from
`@/app/`(prowler)/_overview/_components/ to an appropriate shared location such as
lib/, types/, or hooks/, then update the import statement in
scans-filter-bar.tsx to reference the new shared path, and ensure all other
files that import this component from the old location are also updated to use
the new shared import path.

In `@ui/hooks/use-related-filters.ts`:
- Around line 44-54: The default providerFilterType value in
UseRelatedFiltersProps does not align with the hook's data selection logic. The
hook selects between providerIds (which map to provider_id__in) and providerUIDs
(which map to provider_uid__in) on line 48, but the default providerFilterType
is set to FILTER_FIELD.PROVIDER (provider__in), which is intended for a
different view. Change the default value of providerFilterType from
FILTER_FIELD.PROVIDER to FILTER_FIELD.PROVIDER_ID, and update the type
constraint for providerFilterType in the UseRelatedFiltersProps interface
(around lines 20-22) to allow PROVIDER | PROVIDER_ID | PROVIDER_UID instead of
just PROVIDER | PROVIDER_UID. This ensures the hook reads from the correct URL
parameter key that matches the actual provider data being used.

---

Outside diff comments:
In `@ui/app/`(prowler)/_overview/graphs-tabs/risk-plot/risk-plot.ssr.tsx:
- Around line 31-47: The filtering logic in this section uses an else if chain,
which prevents multiple filters from being applied simultaneously. When
providerIdFilter, providerGroupsFilter, and providerTypeFilter are all present,
only the first matching condition executes, ignoring other active filters. Fix
this by converting the else if statements to independent if statements so that
each filter is applied cumulatively to the filteredProviders array. Start with
all providers and progressively apply each filter condition that is present,
ensuring that when multiple filters are active in the URL parameters, they all
contribute to narrowing the results rather than mutually excluding each other.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 7ca0b3f6-121c-40f2-a478-3ca68ec634b2

📥 Commits

Reviewing files that changed from the base of the PR and between a7917f7 and 9d6ea86.

📒 Files selected for processing (37)
  • ui/actions/finding-groups/finding-groups.ts
  • ui/actions/findings/findings-filters.ts
  • ui/actions/manage-groups/manage-groups.test.ts
  • ui/actions/manage-groups/manage-groups.ts
  • ui/actions/overview/overview-filters.ts
  • ui/actions/providers/providers-filters.ts
  • ui/actions/resources/resources-filters.ts
  • ui/actions/scans/scans-filters.ts
  • ui/app/(prowler)/_overview/_components/provider-group-selector.test.tsx
  • ui/app/(prowler)/_overview/_components/provider-group-selector.tsx
  • ui/app/(prowler)/_overview/graphs-tabs/risk-pipeline-view/risk-pipeline-view.ssr.tsx
  • ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot.ssr.tsx
  • ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx
  • ui/app/(prowler)/findings/page.tsx
  • ui/app/(prowler)/page.tsx
  • ui/app/(prowler)/providers/page.tsx
  • ui/app/(prowler)/providers/providers-page.utils.test.ts
  • ui/app/(prowler)/providers/providers-page.utils.ts
  • ui/app/(prowler)/resources/page.tsx
  • ui/app/(prowler)/scans/page.tsx
  • ui/components/filters/data-filters.ts
  • ui/components/findings/findings-filters.tsx
  • ui/components/findings/findings-filters.utils.test.ts
  • ui/components/findings/findings-filters.utils.ts
  • ui/components/providers/providers-accounts-view.tsx
  • ui/components/providers/providers-filters.test.tsx
  • ui/components/providers/providers-filters.tsx
  • ui/components/resources/resources-filters.tsx
  • ui/components/resources/resources-filters.utils.test.ts
  • ui/components/resources/resources-filters.utils.ts
  • ui/components/scans/scans-filter-bar.test.tsx
  • ui/components/scans/scans-filter-bar.tsx
  • ui/components/scans/scans-page-shell.tsx
  • ui/hooks/use-filter-batch.test.ts
  • ui/hooks/use-related-filters.ts
  • ui/types/filters.ts
  • ui/types/providers-table.ts

Comment thread ui/actions/findings/findings-filters.ts Outdated
Comment thread ui/actions/manage-groups/manage-groups.test.ts
Comment thread ui/actions/manage-groups/manage-groups.ts
Comment thread ui/actions/overview/overview-filters.ts
Comment thread ui/actions/providers/providers-filters.ts
Comment thread ui/components/filters/provider-group-selector.tsx
Comment thread ui/app/(prowler)/scans/page.tsx
Comment thread ui/app/(prowler)/scans/page.tsx
Comment thread ui/components/scans/scans-filter-bar.tsx Outdated
Comment thread ui/hooks/use-related-filters.ts
@pfe-nazaries

Copy link
Copy Markdown
Contributor Author

CodeRabbit review — addressed

Triaged all 12 review threads against the current code. Commit f08d7c9f1 on feature/add-provider-group-filter.

✅ Fixed (6)

Thread Change
manage-groups.ts — partial group data getAllProviderGroups now aborts (returns undefined) when a later page resolves to an API error payload, instead of treating it as "no more pages" and silently truncating
manage-groups.test.ts — regression Added a test for an error payload on page N (asserts undefined, no partial merge)
scans/page.tsx — pending rows Synthetic pending-schedule rows now honor filter[provider_groups__in] via provider group membership, matching backend-filtered scan rows
scans/page.tsx — prefetch Promise.allPromise.allSettled so a transient failure of either provider/group fetch no longer fails the whole route
ProviderGroupSelector relocation (×2) Moved from app/(prowler)/_overview/_components/ to components/filters/ (used across 5 views); all imports and test mocks updated

Verified locally: tsc ✅ · eslint ✅ · affected unit tests 41/41 ✅

⏭️ Not changed, with rationale

Const-object refactor of inline filter-param literals (findings-filters.ts, resources-filters.ts, scans-filters.ts state__in/trigger): kept as inline literal unions. A const-object referenced only in a typeof position trips this repo's own @typescript-eslint/no-unused-vars ("...is assigned a value but only used as a type"). The const-object pattern is therefore reserved for fields that also have a runtime use (e.g. FILTER_FIELD, SCANS_PROVIDER_FILTER_FIELD); inline literals are the lint-clean idiom for type-only param keys here.

overview-filters.ts / providers-filters.ts "inline union": these already derive from const objects via indexed access — (typeof FILTER_FIELD)["PROVIDER_TYPE" | ...] and (typeof PROVIDERS_PAGE_FILTER)[...]. No bare value-union is present, so there is nothing to convert.

use-related-filters.ts default providerFilterType: out of scope for this PR. The diff only renames FilterTypeFILTER_FIELD (identical semantics), and the hook currently has no callers anywhere in ui/, so there is no behavior to change here. Worth a separate look if/when the hook is wired up.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ui/actions/manage-groups/manage-groups.ts (2)

62-70: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move auth-header resolution inside the try block.

Line 62 runs before the try; if getAuthHeaders throws, this action rejects instead of returning undefined, which can take down callers that batch requests.

Suggested fix
 export const getAllProviderGroups = async (): Promise<
   ProviderGroupsResponse | undefined
 > => {
-  const headers = await getAuthHeaders({ contentType: false });
   const pageSize = 100; // Larger page size to minimize API calls
   const maxPages = 50; // Safety limit: 50 pages × 100 = 5000 groups max
   let currentPage = 1;
   const allGroups: ProviderGroupsResponse["data"] = [];
   let lastResponse: ProviderGroupsResponse | undefined;
   let hasMorePages = true;

   try {
+    const headers = await getAuthHeaders({ contentType: false });
     while (hasMorePages && currentPage <= maxPages) {
       const url = new URL(`${apiBaseUrl}/provider-groups`);
       url.searchParams.append("page[number]", currentPage.toString());
       url.searchParams.append("page[size]", pageSize.toString());
🤖 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 `@ui/actions/manage-groups/manage-groups.ts` around lines 62 - 70, The
getAuthHeaders call at the start of the function is outside the try block, so
any errors it throws will reject the action instead of being caught and handled
gracefully. Move the line that calls getAuthHeaders({ contentType: false }) and
assigns the result to the headers variable inside the try block to ensure any
errors are properly caught and the action can return undefined instead of
rejecting, preventing downstream issues for batch request callers.

71-121: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not return partial results when the max-page guard is reached.

If there are still more pages after Line 103 exits due maxPages, the function currently returns a truncated group list. This silently drops filter options.

Suggested fix
     while (hasMorePages && currentPage <= maxPages) {
       ...
     }

+    if (hasMorePages && currentPage > maxPages) {
+      console.error(
+        `Error fetching all provider groups: exceeded max page limit (${maxPages})`,
+      );
+      return undefined;
+    }
+
     if (lastResponse) {
       return {
         ...lastResponse,
         data: allGroups,
🤖 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 `@ui/actions/manage-groups/manage-groups.ts` around lines 71 - 121, The
function currently returns partial results when exiting the while loop at
maxPages while hasMorePages is still true, which silently truncates the group
list. After the while loop completes, check if hasMorePages is still true
(indicating the loop was stopped by the maxPages limit rather than fetching all
available pages). If hasMorePages is true, return undefined to indicate
incomplete results instead of returning the truncated allGroups. Only proceed
with the current return logic (combining allGroups with lastResponse metadata)
when hasMorePages is false, ensuring complete data or no data is returned.
🤖 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 `@ui/actions/manage-groups/manage-groups.ts`:
- Around line 62-70: The getAuthHeaders call at the start of the function is
outside the try block, so any errors it throws will reject the action instead of
being caught and handled gracefully. Move the line that calls getAuthHeaders({
contentType: false }) and assigns the result to the headers variable inside the
try block to ensure any errors are properly caught and the action can return
undefined instead of rejecting, preventing downstream issues for batch request
callers.
- Around line 71-121: The function currently returns partial results when
exiting the while loop at maxPages while hasMorePages is still true, which
silently truncates the group list. After the while loop completes, check if
hasMorePages is still true (indicating the loop was stopped by the maxPages
limit rather than fetching all available pages). If hasMorePages is true, return
undefined to indicate incomplete results instead of returning the truncated
allGroups. Only proceed with the current return logic (combining allGroups with
lastResponse metadata) when hasMorePages is false, ensuring complete data or no
data is returned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 50bc732a-4cbc-4e73-a069-4ee46b954e2a

📥 Commits

Reviewing files that changed from the base of the PR and between 170507a and f08d7c9.

📒 Files selected for processing (13)
  • ui/actions/manage-groups/manage-groups.test.ts
  • ui/actions/manage-groups/manage-groups.ts
  • ui/actions/scans/scans-filters.ts
  • ui/app/(prowler)/page.tsx
  • ui/app/(prowler)/scans/page.tsx
  • ui/components/filters/provider-group-selector.test.tsx
  • ui/components/filters/provider-group-selector.tsx
  • ui/components/findings/findings-filters.tsx
  • ui/components/providers/providers-filters.test.tsx
  • ui/components/providers/providers-filters.tsx
  • ui/components/resources/resources-filters.tsx
  • ui/components/scans/scans-filter-bar.test.tsx
  • ui/components/scans/scans-filter-bar.tsx
💤 Files with no reviewable changes (2)
  • ui/components/filters/provider-group-selector.test.tsx
  • ui/components/filters/provider-group-selector.tsx

@Alan-TheGentleman

Copy link
Copy Markdown
Contributor

The group + account combination silently drops the group filter. In risk-pipeline-view.ssr.tsx and risk-plot.ssr.tsx, the providerIdFilter branch rebuilds the filters object from scratch and never re-adds provider_groups__in, so selecting a group then an account ignores the group. Both SSR helpers are untested, which is why a branch like this regresses quietly. Could we add unit tests on the per-branch filter building, including the empty-group case?

Duplication worth folding before it spreads:

  • getProviderGroupDisplayValue is identical in findings-filters.utils.ts and resources-filters.utils.ts. Pull it into a shared util.
  • The groups -> providers logic is duplicated in both SSR files. A shared filterProvidersByGroups(providers, groupIds) keeps them in sync.
  • risk-plot.ssr.tsx and finding-severity-over-time.tsx still use the literal "filter[provider_groups__in]" instead of OVERVIEW_FILTER_PARAM.PROVIDER_GROUPS, so a later rename breaks them with no type error.

Worth verifying (not blocking): getAllProviderGroups paginates sequentially across all five views (confirm there's a server-side cap), truncates silently at 5000 groups (surface a signal instead), and forwards URL UUIDs without the validation provider_type__in gets.

Happy to pair on the tests if that helps.

Pablo F.G and others added 2 commits June 24, 2026 09:12
Resolve conflicts in favor of the FILTER_FIELD refactor: migrate
master's new FilterType usages (AccountFilterKey, account selectors,
scans) to FILTER_FIELD, adopt master's id-based scans account filter
while keeping the provider-group filter, and move the #11659 changelog
entry to a new 1.32.0 UNRELEASED block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@alejandrobailo alejandrobailo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Really nice work, the refactor is clean and the test coverage is solid. A few small things I'd like to sort out before merging, mostly consistency cleanups, none of them blockers on their own:

  1. In scans/page.tsx you switched to Promise.allSettled, but the other pages still use Promise.all. Both getAllProviders and getAllProviderGroups already catch internally and return undefined, so they never reject, which means the .status === "fulfilled" branches here are dead code. I'd go back to Promise.all to keep it consistent with findings/resources/overview.

  2. In provider-group-selector.tsx the filter key is hardcoded as "provider_groups__in" instead of reusing FILTER_FIELD.PROVIDER_GROUPS. Same value today, but it can drift over time and TS won't catch it. Let's reference the constant.

  3. The selector uses hardcoded DOM ids (provider-group-selector / provider-group-label). No page renders two of these today so it works, but AccountsSelector already takes an id prop for exactly this reason. I'd add one here too just to be safe.

  4. In the providerTypeFilter branch of risk-pipeline-view.ssr.tsx we enumerate the types straight from the filter without intersecting with scopedProviders, so we rely on the API param to scope by group. It's not a data leak, but we can end up firing redundant calls or showing an empty node for a type that has no providers in the selected group. Worth a second look.

  5. parseFilterIds doesn't validate the group ids coming from the URL. Low impact since the backend UUIDInFilter rejects malformed uuids with a 400, but leaving it noted.

- Restore Promise.all in scans page (getAll* never reject)
- Use FILTER_FIELD.PROVIDER_GROUPS instead of a hardcoded key
- Add id prop to ProviderGroupSelector to avoid DOM id collisions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pfe-nazaries

Copy link
Copy Markdown
Contributor Author

Really nice work, the refactor is clean and the test coverage is solid. A few small things I'd like to sort out before merging, mostly consistency cleanups, none of them blockers on their own:

  1. In scans/page.tsx you switched to Promise.allSettled, but the other pages still use Promise.all. Both getAllProviders and getAllProviderGroups already catch internally and return undefined, so they never reject, which means the .status === "fulfilled" branches here are dead code. I'd go back to Promise.all to keep it consistent with findings/resources/overview.
  2. In provider-group-selector.tsx the filter key is hardcoded as "provider_groups__in" instead of reusing FILTER_FIELD.PROVIDER_GROUPS. Same value today, but it can drift over time and TS won't catch it. Let's reference the constant.
  3. The selector uses hardcoded DOM ids (provider-group-selector / provider-group-label). No page renders two of these today so it works, but AccountsSelector already takes an id prop for exactly this reason. I'd add one here too just to be safe.
  4. In the providerTypeFilter branch of risk-pipeline-view.ssr.tsx we enumerate the types straight from the filter without intersecting with scopedProviders, so we rely on the API param to scope by group. It's not a data leak, but we can end up firing redundant calls or showing an empty node for a type that has no providers in the selected group. Worth a second look.
  5. parseFilterIds doesn't validate the group ids coming from the URL. Low impact since the backend UUIDInFilter rejects malformed uuids with a 400, but leaving it noted.
  • 1 to 3 applied
  • 4 is intended as I discussed with @AdriiiPRodri, we will iterate on this
  • 5 deferred

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants