Files
prowler/ui/lib/provider-filters.ts

145 lines
3.9 KiB
TypeScript

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,
});