Files
KIS-TOiR/server/src/modules/shared/query-utils.ts

104 lines
3.2 KiB
TypeScript

import type { Response } from 'express';
import { Prisma } from '@prisma/client';
export type QueryValue = unknown;
export function getSingle(value: QueryValue): string | undefined {
if (Array.isArray(value)) {
const first = value[0];
return typeof first === 'string' ? first : first === undefined || first === null ? undefined : String(first);
}
if (typeof value === 'string') {
return value;
}
if (value === undefined || value === null) {
return undefined;
}
return String(value);
}
export function getFirst(value: QueryValue): string | undefined {
return getSingle(value);
}
export function getStringArray(value: QueryValue): string[] {
if (Array.isArray(value)) {
return value
.filter((item) => item !== undefined && item !== null && item !== '')
.map((item) => String(item));
}
if (value === undefined || value === null || value === '') {
return [];
}
return [String(value)];
}
export function toArray(value: QueryValue): string[] {
return getStringArray(value);
}
export function parseRange(query: Record<string, QueryValue>): { start: number; end: number; take: number } {
const start = Number(getFirst(query._start) ?? 0);
const end = Number(getFirst(query._end) ?? start + 25);
const safeStart = Number.isFinite(start) && start >= 0 ? start : 0;
const safeEnd = Number.isFinite(end) && end > safeStart ? end : safeStart + 25;
return {
start: safeStart,
end: safeEnd,
take: safeEnd - safeStart,
};
}
export function toSortOrder(value: string | undefined): 'asc' | 'desc' {
return String(value ?? 'ASC').toLowerCase() === 'desc' ? 'desc' : 'asc';
}
export function containsInsensitive(value: string) {
return {
contains: value,
mode: 'insensitive' as const,
};
}
export function toNumberValue(value: unknown): number | null | undefined {
if (value === undefined) return undefined;
if (value === null || value === '') return null;
const numeric = Number(value);
return Number.isNaN(numeric) ? undefined : numeric;
}
export function toDateValue(value: unknown): Date | null | undefined {
if (value === undefined) return undefined;
if (value === null || value === '') return null;
return new Date(String(value));
}
export function toDecimalValue(value: unknown): Prisma.Decimal | null | undefined {
if (value === undefined) return undefined;
if (value === null || value === '') return null;
return new Prisma.Decimal(String(value));
}
export function setListHeaders(
response: Response,
totalOrResource: number | string,
start: number,
end: number,
resourceOrTotal: string | number,
): void {
const total = typeof totalOrResource === 'number' ? totalOrResource : Number(resourceOrTotal);
const resource = typeof totalOrResource === 'string' ? totalOrResource : String(resourceOrTotal);
const safeStart = Number.isFinite(start) ? start : 0;
const safeEnd = Number.isFinite(end) ? end : safeStart;
const contentEnd = total === 0 ? safeStart : Math.min(Math.max(safeEnd - 1, safeStart), Math.max(total - 1, safeStart));
response.setHeader('Content-Range', `${resource} ${safeStart}-${contentEnd}/${total}`);
response.setHeader('Access-Control-Expose-Headers', 'Content-Range');
}