231 lines
6.6 KiB
TypeScript
231 lines
6.6 KiB
TypeScript
/**
|
|
* @file SearchParamParser.ts
|
|
* Type-safe parser for search parameters from URL
|
|
* Returns Result type for clean error handling
|
|
*/
|
|
|
|
import { Result } from '@/lib/contracts/Result';
|
|
import { SearchParamValidators } from './SearchParamValidators';
|
|
|
|
export interface ParsedAuthParams {
|
|
returnTo?: string | null;
|
|
token?: string | null;
|
|
email?: string | null;
|
|
error?: string | null;
|
|
message?: string | null;
|
|
}
|
|
|
|
export interface ParsedSponsorParams {
|
|
type?: string | null;
|
|
campaignId?: string | null;
|
|
}
|
|
|
|
export interface ParsedPaginationParams {
|
|
page?: number;
|
|
limit?: number;
|
|
offset?: number;
|
|
}
|
|
|
|
export interface ParsedSortingParams {
|
|
sortBy?: string | null;
|
|
order?: 'asc' | 'desc';
|
|
}
|
|
|
|
export interface ParsedFilterParams {
|
|
status?: string | null;
|
|
role?: string | null;
|
|
tier?: string | null;
|
|
}
|
|
|
|
export interface ParsedWizardParams {
|
|
step?: string | null;
|
|
}
|
|
|
|
export class SearchParamParser {
|
|
private static getParam(params: URLSearchParams | Record<string, string | string[] | undefined>, key: string): string | null {
|
|
if (params instanceof URLSearchParams) {
|
|
return params.get(key);
|
|
}
|
|
const value = params[key];
|
|
if (Array.isArray(value)) {
|
|
return value[0] ?? null;
|
|
}
|
|
return value ?? null;
|
|
}
|
|
|
|
// Parse auth parameters
|
|
static parseAuth(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedAuthParams, string> {
|
|
const errors: string[] = [];
|
|
|
|
const returnTo = this.getParam(params, 'returnTo');
|
|
if (returnTo !== null) {
|
|
const validation = SearchParamValidators.validateReturnTo(returnTo);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
}
|
|
}
|
|
|
|
const token = this.getParam(params, 'token');
|
|
if (token !== null) {
|
|
const validation = SearchParamValidators.validateToken(token);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
}
|
|
}
|
|
|
|
const email = this.getParam(params, 'email');
|
|
if (email !== null) {
|
|
const validation = SearchParamValidators.validateEmail(email);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
}
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
return Result.err(errors.join(', '));
|
|
}
|
|
|
|
return Result.ok({
|
|
returnTo: this.getParam(params, 'returnTo'),
|
|
token: this.getParam(params, 'token'),
|
|
email: this.getParam(params, 'email'),
|
|
error: this.getParam(params, 'error'),
|
|
message: this.getParam(params, 'message'),
|
|
});
|
|
}
|
|
|
|
// Parse sponsor parameters
|
|
static parseSponsor(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedSponsorParams, string> {
|
|
const errors: string[] = [];
|
|
|
|
const type = this.getParam(params, 'type');
|
|
if (type !== null) {
|
|
const validation = SearchParamValidators.validateCampaignType(type);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
}
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
return Result.err(errors.join(', '));
|
|
}
|
|
|
|
return Result.ok({
|
|
type: this.getParam(params, 'type'),
|
|
campaignId: this.getParam(params, 'campaignId'),
|
|
});
|
|
}
|
|
|
|
// Parse pagination parameters
|
|
static parsePagination(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedPaginationParams, string> {
|
|
const result: ParsedPaginationParams = {};
|
|
const errors: string[] = [];
|
|
|
|
const page = this.getParam(params, 'page');
|
|
if (page !== null) {
|
|
const validation = SearchParamValidators.validatePage(page);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
} else {
|
|
result.page = parseInt(page);
|
|
}
|
|
}
|
|
|
|
const limit = this.getParam(params, 'limit');
|
|
if (limit !== null) {
|
|
const validation = SearchParamValidators.validateLimit(limit);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
} else {
|
|
result.limit = parseInt(limit);
|
|
}
|
|
}
|
|
|
|
const offset = this.getParam(params, 'offset');
|
|
if (offset !== null) {
|
|
const num = parseInt(offset);
|
|
if (!isNaN(num)) {
|
|
result.offset = num;
|
|
}
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
return Result.err(errors.join(', '));
|
|
}
|
|
|
|
return Result.ok(result);
|
|
}
|
|
|
|
// Parse sorting parameters
|
|
static parseSorting(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedSortingParams, string> {
|
|
const errors: string[] = [];
|
|
|
|
const order = this.getParam(params, 'order');
|
|
if (order !== null) {
|
|
const validation = SearchParamValidators.validateOrder(order);
|
|
if (!validation.isValid) {
|
|
errors.push(...validation.errors);
|
|
}
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
return Result.err(errors.join(', '));
|
|
}
|
|
|
|
return Result.ok({
|
|
sortBy: this.getParam(params, 'sortBy'),
|
|
order: (this.getParam(params, 'order') as 'asc' | 'desc') || undefined,
|
|
});
|
|
}
|
|
|
|
// Parse filter parameters
|
|
static parseFilters(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedFilterParams, string> {
|
|
return Result.ok({
|
|
status: this.getParam(params, 'status'),
|
|
role: this.getParam(params, 'role'),
|
|
tier: this.getParam(params, 'tier'),
|
|
});
|
|
}
|
|
|
|
// Parse wizard parameters
|
|
static parseWizard(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedWizardParams, string> {
|
|
return Result.ok({
|
|
step: this.getParam(params, 'step'),
|
|
});
|
|
}
|
|
|
|
// Parse all parameters at once
|
|
static parseAll(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<
|
|
{
|
|
auth: ParsedAuthParams;
|
|
sponsor: ParsedSponsorParams;
|
|
pagination: ParsedPaginationParams;
|
|
sorting: ParsedSortingParams;
|
|
filters: ParsedFilterParams;
|
|
},
|
|
string
|
|
> {
|
|
const authResult = this.parseAuth(params);
|
|
if (authResult.isErr()) return Result.err(authResult.getError());
|
|
|
|
const sponsorResult = this.parseSponsor(params);
|
|
if (sponsorResult.isErr()) return Result.err(sponsorResult.getError());
|
|
|
|
const paginationResult = this.parsePagination(params);
|
|
if (paginationResult.isErr()) return Result.err(paginationResult.getError());
|
|
|
|
const sortingResult = this.parseSorting(params);
|
|
if (sortingResult.isErr()) return Result.err(sortingResult.getError());
|
|
|
|
const filtersResult = this.parseFilters(params);
|
|
|
|
return Result.ok({
|
|
auth: authResult.unwrap(),
|
|
sponsor: sponsorResult.unwrap(),
|
|
pagination: paginationResult.unwrap(),
|
|
sorting: sortingResult.unwrap(),
|
|
filters: filtersResult.unwrap(),
|
|
});
|
|
}
|
|
}
|