UNPKG

@cloud-ru/ft-request-payload-transform

Version:
167 lines (129 loc) 4.85 kB
import { ARRAY_REG, DELIMITER, MULTI_VALUE_CONDITIONS, PAGINATION_KEYS } from './constants'; import { FieldFilter, FieldSort, PaginationParams, RequestPayloadParams } from './types'; const OBJECT_FORMAT_REGEX = /^[a-zA-Z\d]+:(,|[a-zA-Z\d]+).*/; function isStringArray(str: string) { return ARRAY_REG.test(str); } function isObject(value: unknown): value is Record<string, unknown> { return value !== null && typeof value === 'object' && !Array.isArray(value); } function parseStringToTypedValue(value: string) { const valueAsNumber = Number(value); const valueAsBooleanOrString = ['true', 'false'].includes(value) ? value === 'true' : value; return isNaN(valueAsNumber) ? valueAsBooleanOrString : valueAsNumber; } function stringifySortParams(sortParams: FieldSort[]) { if (sortParams.length === 0) { return ''; } const formattedFields = sortParams.map(({ field, direction }) => `${direction}${field}`); return `[${formattedFields.join(',')}]`; } function stringifyFilterParams(filters: FieldFilter[]) { return filters.reduce((acc, cur, ind) => { const delimiter = ind !== 0 && ind !== filters.length ? DELIMITER : ''; const { field, condition, value } = cur; let formattedValue = value; if (MULTI_VALUE_CONDITIONS.includes(condition as (typeof MULTI_VALUE_CONDITIONS)[number]) && Array.isArray(value)) { formattedValue = `[${value.join(',')}]`; } if (isObject(value)) { formattedValue = Object.entries(value).reduce( (acc, [key, value], ind, arr) => `${acc}${key}:${value}${ind === arr.length - 1 ? '' : ','}`, '', ); } return `${acc}${delimiter}${field}[${condition}]${formattedValue}`; }, ''); } export function isValueNotEmpty(value: unknown) { if (typeof value === 'number') { return !isNaN(value); } if (typeof value === 'string' || Array.isArray(value)) { return value.length !== 0; } if (isObject(value)) { return Object.keys(value).length !== 0; } return value !== undefined && value !== null; } export function stringifyRequestParams( params: Partial<Omit<RequestPayloadParams, 'pagination'>> & { pagination?: Partial<PaginationParams> }, { encode = false }: { encode?: boolean }, ) { const { pagination, ...restParams } = params; const result: Partial<Record<keyof Omit<RequestPayloadParams, 'pagination'> | keyof PaginationParams, unknown>> = { ...pagination, ...restParams, }; if (params.filter?.length) { result.filter = stringifyFilterParams(params.filter); } if (params.ordering?.length) { result.ordering = stringifySortParams(params.ordering); } const returnString = Object.entries(result).reduce((acc, [key, value]) => { const delimiter = acc.length ? '&' : ''; return `${acc}${delimiter}${key}=${value}`; }, ''); return encode ? encodeURIComponent(returnString) : returnString; } export function isPaginationKey(key: string): key is keyof PaginationParams { return Object.values(PAGINATION_KEYS).includes(key); } export function parseStringToValue(filterValue: string) { if (filterValue === undefined) { return undefined; } if (isStringArray(filterValue)) { try { return JSON.parse(filterValue); } catch { return filterValue .replace(/[[\]]/g, '') .split(',') .map(v => v.trim()); } } // Check if value represents an object in format "key1:value1,key2:value2,..." const isObjectString = OBJECT_FORMAT_REGEX.test(filterValue); if (isObjectString) { const parsedValue = filterValue.split(',').reduce<Record<string, string | number | boolean>>((acc, cur) => { const [key, value] = cur.split(':'); if (key !== undefined && value !== undefined) { acc[key] = parseStringToTypedValue(value); } return acc; }, {}); return Object.values(parsedValue).length ? parsedValue : undefined; } return parseStringToTypedValue(filterValue); } export function removeEmptyValuesFromObject<T extends Partial<RequestPayloadParams | PaginationParams | FieldFilter>>( obj: T, ) { return Object.keys(obj).reduce<T>((acc, cur) => { const key = cur as keyof T; let value = obj[key]; if (isValueNotEmpty(value)) { if (isObject(value)) { value = removeEmptyValuesFromObject(value); } if (Array.isArray(value)) { // Special handling for filter arrays - remove filters with empty values if (key === 'filter') { value = value.filter((filterItem: FieldFilter) => isValueNotEmpty(filterItem?.value)) as T[keyof T]; } else { value = value.filter(isValueNotEmpty) as T[keyof T]; } if ((value as T[keyof T][]).length > 0) { acc[key] = value; } } else { acc[key] = value; } } return acc; }, {} as T); }