mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-21 18:58:04 +00:00
refactor(ui): centralize provider type filter sanitization in server actions (#10043)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
export const getFindings = async ({
|
||||
page = 1,
|
||||
@@ -24,12 +25,7 @@ export const getFindings = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
// Skip filter[search] since it's already added via the `query` param above
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const findings = await fetch(url.toString(), {
|
||||
@@ -65,12 +61,7 @@ export const getLatestFindings = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
// Skip filter[search] since it's already added via the `query` param above
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const findings = await fetch(url.toString(), {
|
||||
@@ -96,20 +87,13 @@ export const getMetadataInfo = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
// Define filters to exclude
|
||||
const excludedFilters = [
|
||||
appendSanitizedProviderTypeFilters(url, filters, {
|
||||
excludedKeyIncludes: [
|
||||
"region__in",
|
||||
"service__in",
|
||||
"resource_type__in",
|
||||
"resource_groups__in",
|
||||
];
|
||||
if (
|
||||
key !== "filter[search]" &&
|
||||
!excludedFilters.some((filter) => key.includes(filter))
|
||||
) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -136,20 +120,13 @@ export const getLatestMetadataInfo = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
// Define filters to exclude
|
||||
const excludedFilters = [
|
||||
appendSanitizedProviderTypeFilters(url, filters, {
|
||||
excludedKeyIncludes: [
|
||||
"region__in",
|
||||
"service__in",
|
||||
"resource_type__in",
|
||||
"resource_groups__in",
|
||||
];
|
||||
if (
|
||||
key !== "filter[search]" &&
|
||||
!excludedFilters.some((filter) => key.includes(filter))
|
||||
) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { AttackSurfaceOverviewResponse } from "./types";
|
||||
@@ -14,12 +15,7 @@ export const getAttackSurfaceOverview = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/attack-surfaces`);
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { ComplianceWatchlistResponse } from "./compliance-watchlist.types";
|
||||
@@ -15,11 +16,7 @@ export const getComplianceWatchlist = async ({
|
||||
|
||||
// Append filter parameters (provider_id, provider_type, etc.)
|
||||
// Exclude filter[search] as this endpoint doesn't support text search
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), { headers });
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { FindingsSeverityOverviewResponse } from "./types";
|
||||
@@ -28,17 +29,8 @@ export const getFindingsByStatus = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
// Handle multiple filters, but exclude muted filter as overviews endpoint doesn't support it
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
// The overviews/findings endpoint does not support status or muted filters
|
||||
// (allowed filters include date, region, provider fields). Exclude unsupported ones.
|
||||
if (
|
||||
key !== "filter[search]" &&
|
||||
key !== "filter[muted]" &&
|
||||
key !== "filter[status]"
|
||||
) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
appendSanitizedProviderTypeFilters(url, filters, {
|
||||
excludedKeys: ["filter[search]", "filter[muted]", "filter[status]"],
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -62,15 +54,8 @@ export const getFindingsBySeverity = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/findings_severity`);
|
||||
|
||||
// Handle multiple filters, but exclude unsupported filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (
|
||||
key !== "filter[search]" &&
|
||||
key !== "filter[muted]" &&
|
||||
value !== undefined
|
||||
) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
appendSanitizedProviderTypeFilters(url, filters, {
|
||||
excludedKeys: ["filter[search]", "filter[muted]"],
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { ProvidersOverviewResponse } from "./types";
|
||||
@@ -28,11 +29,7 @@ export const getProvidersOverview = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { RegionsOverviewResponse } from "./types";
|
||||
@@ -14,12 +15,7 @@ export const getRegionsOverview = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/regions`);
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { ResourceGroupOverviewResponse } from "./types";
|
||||
@@ -14,12 +15,7 @@ export const getResourceGroupOverview = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/resource-groups`);
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { CategoryOverviewResponse } from "./types";
|
||||
@@ -14,12 +15,7 @@ export const getCategoryOverview = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/categories`);
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { ServicesOverviewResponse } from "./types";
|
||||
@@ -14,11 +15,7 @@ export const getServicesOverview = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/services`);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]" && value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type TimeRange,
|
||||
} from "@/app/(prowler)/_overview/severity-over-time/_constants/time-range.constants";
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
import { adaptSeverityTrendsResponse } from "./severity-trends.adapter";
|
||||
@@ -27,11 +28,7 @@ const getFindingsSeverityTrends = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/findings_severity/timeseries`);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
export const getThreatScore = async ({
|
||||
@@ -12,12 +13,7 @@ export const getThreatScore = async ({
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/overviews/threatscore`);
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { redirect } from "next/navigation";
|
||||
import { apiBaseUrl, getAuthHeaders, getFormValue, wait } from "@/lib";
|
||||
import { buildSecretConfig } from "@/lib/provider-credentials/build-crendentials";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { appendSanitizedProviderInFilters } from "@/lib/provider-filters";
|
||||
import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper";
|
||||
import { ProvidersApiResponse, ProviderType } from "@/types/providers";
|
||||
|
||||
@@ -15,6 +16,12 @@ export const getProviders = async ({
|
||||
sort = "",
|
||||
filters = {},
|
||||
pageSize = 10,
|
||||
}: {
|
||||
page?: number;
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
pageSize?: number;
|
||||
}): Promise<ProvidersApiResponse | undefined> => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
|
||||
@@ -27,12 +34,7 @@ export const getProviders = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderInFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
@@ -60,7 +62,7 @@ export const getAllProviders = async ({
|
||||
}: {
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, unknown>;
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
} = {}): Promise<ProvidersApiResponse | undefined> => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
const pageSize = 100; // Use larger page size to minimize API calls
|
||||
@@ -79,11 +81,7 @@ export const getAllProviders = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderInFilters(url, filters);
|
||||
|
||||
const response = await fetch(url.toString(), { headers });
|
||||
const data = (await handleApiResponse(response)) as
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
|
||||
export const getResources = async ({
|
||||
@@ -17,7 +18,7 @@ export const getResources = async ({
|
||||
page?: number;
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, string>;
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
pageSize?: number;
|
||||
include?: string;
|
||||
fields?: string[];
|
||||
@@ -38,9 +39,7 @@ export const getResources = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, String(value));
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
@@ -66,7 +65,7 @@ export const getLatestResources = async ({
|
||||
page?: number;
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, string>;
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
pageSize?: number;
|
||||
include?: string;
|
||||
fields?: string[];
|
||||
@@ -87,9 +86,7 @@ export const getLatestResources = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, String(value));
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
@@ -107,6 +104,10 @@ export const getMetadataInfo = async ({
|
||||
query = "",
|
||||
sort = "",
|
||||
filters = {},
|
||||
}: {
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
}) => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
|
||||
@@ -115,9 +116,7 @@ export const getMetadataInfo = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, String(value));
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
@@ -135,6 +134,10 @@ export const getLatestMetadataInfo = async ({
|
||||
query = "",
|
||||
sort = "",
|
||||
filters = {},
|
||||
}: {
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
}) => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
|
||||
@@ -143,9 +146,7 @@ export const getLatestMetadataInfo = async ({
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, String(value));
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -7,6 +7,10 @@ import {
|
||||
COMPLIANCE_REPORT_DISPLAY_NAMES,
|
||||
type ComplianceReportType,
|
||||
} from "@/lib/compliance/compliance-report-types";
|
||||
import {
|
||||
appendSanitizedProviderTypeFilters,
|
||||
sanitizeProviderTypesCsv,
|
||||
} from "@/lib/provider-filters";
|
||||
import { addScanOperation } from "@/lib/sentry-breadcrumbs";
|
||||
import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper";
|
||||
export const getScans = async ({
|
||||
@@ -35,13 +39,7 @@ export const getScans = async ({
|
||||
url.searchParams.append(`fields[${key}]`, String(value));
|
||||
});
|
||||
|
||||
// Add dynamic filters (e.g., "filter[state]", "fields[scans]")
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
// Skip filter[search] since it's already added via the `query` param above
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
appendSanitizedProviderTypeFilters(url, filters);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), { headers });
|
||||
@@ -58,6 +56,10 @@ export const getScansByState = async () => {
|
||||
const url = new URL(`${apiBaseUrl}/scans`);
|
||||
// Request only the necessary fields to optimize the response
|
||||
url.searchParams.append("fields[scans]", "state");
|
||||
url.searchParams.append(
|
||||
"filter[provider_type__in]",
|
||||
sanitizeProviderTypesCsv(),
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "@/components/providers/table";
|
||||
import { ContentLayout } from "@/components/ui";
|
||||
import { DataTable } from "@/components/ui/table";
|
||||
import { PROVIDER_TYPES, ProviderProps, SearchParamsProps } from "@/types";
|
||||
import { ProviderProps, SearchParamsProps } from "@/types";
|
||||
|
||||
export default async function Providers({
|
||||
searchParams,
|
||||
@@ -89,22 +89,15 @@ const ProvidersTable = async ({
|
||||
return acc;
|
||||
}, {}) || {};
|
||||
|
||||
// Exclude provider types not yet supported in the UI
|
||||
const enrichedProviders =
|
||||
providersData?.data
|
||||
?.filter((provider: ProviderProps) =>
|
||||
(PROVIDER_TYPES as readonly string[]).includes(
|
||||
provider.attributes.provider,
|
||||
),
|
||||
)
|
||||
.map((provider: ProviderProps) => {
|
||||
const groupNames =
|
||||
provider.relationships?.provider_groups?.data?.map(
|
||||
(group: { id: string }) =>
|
||||
providerGroupDict[group.id] || "Unknown Group",
|
||||
) || [];
|
||||
return { ...provider, groupNames };
|
||||
}) || [];
|
||||
providersData?.data?.map((provider: ProviderProps) => {
|
||||
const groupNames =
|
||||
provider.relationships?.provider_groups?.data?.map(
|
||||
(group: { id: string }) =>
|
||||
providerGroupDict[group.id] || "Unknown Group",
|
||||
) || [];
|
||||
return { ...provider, groupNames };
|
||||
}) || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
144
ui/lib/provider-filters.ts
Normal file
144
ui/lib/provider-filters.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { PROVIDER_TYPES, type ProviderType } from "@/types/providers";
|
||||
|
||||
export type ProviderFilterValue = string | string[] | undefined;
|
||||
export type ProviderFilters = Record<string, ProviderFilterValue>;
|
||||
|
||||
interface AppendProviderFiltersOptions {
|
||||
ensuredInFilterKey?: string;
|
||||
excludedKeys?: string[];
|
||||
excludedKeyIncludes?: string[];
|
||||
}
|
||||
|
||||
type AppendSanitizedProviderTypeFiltersOptions = Omit<
|
||||
AppendProviderFiltersOptions,
|
||||
"ensuredInFilterKey"
|
||||
>;
|
||||
|
||||
const SUPPORTED_PROVIDER_TYPES_CSV = PROVIDER_TYPES.join(",");
|
||||
export const PROVIDER_IN_FILTER_KEY = "filter[provider__in]";
|
||||
export const PROVIDER_TYPE_IN_FILTER_KEY = "filter[provider_type__in]";
|
||||
|
||||
const PROVIDER_TYPE_IN_KEYS = new Set([
|
||||
PROVIDER_TYPE_IN_FILTER_KEY,
|
||||
"provider_type__in",
|
||||
]);
|
||||
|
||||
const PROVIDER_SINGLE_KEYS = new Set([
|
||||
"filter[provider_type]",
|
||||
"provider_type",
|
||||
]);
|
||||
|
||||
const toCsvString = (value: unknown): string => {
|
||||
if (Array.isArray(value)) return value.join(",");
|
||||
if (typeof value === "string") return value;
|
||||
return "";
|
||||
};
|
||||
|
||||
const isSupportedProviderType = (value: string): value is ProviderType =>
|
||||
(PROVIDER_TYPES as readonly string[]).includes(value);
|
||||
|
||||
export const sanitizeProviderTypesCsv = (value?: unknown): string => {
|
||||
const rawValue = toCsvString(value);
|
||||
if (!rawValue.trim()) return SUPPORTED_PROVIDER_TYPES_CSV;
|
||||
|
||||
const supportedProviderTypes = Array.from(
|
||||
new Set(
|
||||
rawValue
|
||||
.split(",")
|
||||
.map((providerType) => providerType.trim())
|
||||
.filter(isSupportedProviderType),
|
||||
),
|
||||
);
|
||||
|
||||
return supportedProviderTypes.length > 0
|
||||
? supportedProviderTypes.join(",")
|
||||
: SUPPORTED_PROVIDER_TYPES_CSV;
|
||||
};
|
||||
|
||||
export const sanitizeProviderType = (
|
||||
value?: unknown,
|
||||
): ProviderType | undefined => {
|
||||
const rawValue = toCsvString(value);
|
||||
if (!rawValue.trim()) return undefined;
|
||||
|
||||
return rawValue
|
||||
.split(",")
|
||||
.map((providerType) => providerType.trim())
|
||||
.find(isSupportedProviderType);
|
||||
};
|
||||
|
||||
export const sanitizeProviderFilters = (
|
||||
filters: ProviderFilters = {},
|
||||
ensuredInFilterKey?: string,
|
||||
): ProviderFilters => {
|
||||
const sanitizedFilters: ProviderFilters = { ...filters };
|
||||
|
||||
Object.keys(sanitizedFilters).forEach((key) => {
|
||||
if (PROVIDER_TYPE_IN_KEYS.has(key)) {
|
||||
sanitizedFilters[key] = sanitizeProviderTypesCsv(sanitizedFilters[key]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PROVIDER_SINGLE_KEYS.has(key)) {
|
||||
const providerType = sanitizeProviderType(sanitizedFilters[key]);
|
||||
if (providerType) {
|
||||
sanitizedFilters[key] = providerType;
|
||||
} else {
|
||||
delete sanitizedFilters[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (ensuredInFilterKey) {
|
||||
sanitizedFilters[ensuredInFilterKey] = sanitizeProviderTypesCsv(
|
||||
sanitizedFilters[ensuredInFilterKey],
|
||||
);
|
||||
}
|
||||
|
||||
return sanitizedFilters;
|
||||
};
|
||||
|
||||
export const appendSanitizedProviderFilters = (
|
||||
url: URL,
|
||||
filters: ProviderFilters = {},
|
||||
{
|
||||
ensuredInFilterKey,
|
||||
excludedKeys = ["filter[search]"],
|
||||
excludedKeyIncludes = [],
|
||||
}: AppendProviderFiltersOptions = {},
|
||||
): void => {
|
||||
const sanitizedFilters = sanitizeProviderFilters(filters, ensuredInFilterKey);
|
||||
const excludedKeysSet = new Set(excludedKeys);
|
||||
|
||||
Object.entries(sanitizedFilters).forEach(([key, value]) => {
|
||||
if (
|
||||
value === undefined ||
|
||||
excludedKeysSet.has(key) ||
|
||||
excludedKeyIncludes.some((excludedKey) => key.includes(excludedKey))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
url.searchParams.append(key, String(value));
|
||||
});
|
||||
};
|
||||
|
||||
export const appendSanitizedProviderTypeFilters = (
|
||||
url: URL,
|
||||
filters: ProviderFilters = {},
|
||||
options: AppendSanitizedProviderTypeFiltersOptions = {},
|
||||
): void =>
|
||||
appendSanitizedProviderFilters(url, filters, {
|
||||
...options,
|
||||
ensuredInFilterKey: PROVIDER_TYPE_IN_FILTER_KEY,
|
||||
});
|
||||
|
||||
export const appendSanitizedProviderInFilters = (
|
||||
url: URL,
|
||||
filters: ProviderFilters = {},
|
||||
options: AppendSanitizedProviderTypeFiltersOptions = {},
|
||||
): void =>
|
||||
appendSanitizedProviderFilters(url, filters, {
|
||||
...options,
|
||||
ensuredInFilterKey: PROVIDER_IN_FILTER_KEY,
|
||||
});
|
||||
Reference in New Issue
Block a user