Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/modules/creator/creator.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { parseCreatorSortOptions } from './creator.utils';
import { safeIntParam } from '../../utils/query.utils';
import { parsePublicQuery } from '../../utils/public-query-parse.utils';
import { wrapPublicCreatorListResponse } from '../creators/public-creator-list-envelope.utils';
import { resolveCreatorListLimit } from '../creators/creators.limit.utils';
import { buildCreatorListRequestContext } from '../creators/creator-list-context.utils';
import {
MIN_PAGE_SIZE,
Expand All @@ -27,7 +28,7 @@ const LegacyCreatorQuerySchema = z.object({
label: 'Page',
}),
limit: safeIntParam({
defaultValue: PUBLIC_PAGE_PAGINATION_DEFAULTS.limit,
defaultValue: resolveCreatorListLimit() ?? PUBLIC_PAGE_PAGINATION_DEFAULTS.limit,
min: MIN_PAGE_SIZE,
max: MAX_PAGE_SIZE,
label: 'Limit',
Expand All @@ -40,11 +41,12 @@ export async function listCreators(req: Request, res: Response) {
try {
const ctx = buildCreatorListRequestContext(req);
const parsed = parsePublicQuery(LegacyCreatorQuerySchema, ctx.query);

if (!parsed.ok) {
return sendValidationError(res, 'Invalid query parameters', parsed.details);
}
const { page, limit, sortBy, sortOrder } = parsed.data;

const { page, limit, sortBy, sortOrder } = parsed.data;
const sort = parseCreatorSortOptions(sortBy, sortOrder);

const { creators, meta } = await getPaginatedCreators({
Expand Down
10 changes: 10 additions & 0 deletions src/modules/creators/creators.limit.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { DEFAULT_PAGE_SIZE } from '../../constants/pagination.constants';

/**
* Resolve list limit for public creator list endpoints.
*
* Returns the shared default page size when the incoming page size is omitted.
*/
export function resolveCreatorListLimit(pageSize?: number): number {
return pageSize ?? DEFAULT_PAGE_SIZE;
}
17 changes: 15 additions & 2 deletions src/modules/creators/creators.schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ import { creatorListSortDirectionQueryParam } from './creators.sort-direction.pa
import { withCreatorListQueryStringNormalization } from './creators.query-string.utils';
import { safeIntParam } from '../../utils/query.utils';
import {

import {
MIN_PAGE_SIZE,
MAX_PAGE_SIZE,
} from '../../constants/pagination.constants';

MIN_PAGE_SIZE,
MAX_PAGE_SIZE,
} from '../../constants/pagination.constants';
import { PUBLIC_OFFSET_PAGINATION_DEFAULTS } from '../../utils/public-list-query-defaults';
import { DEFAULT_CREATOR_LIST_SORT } from '../../constants/creator-list-sort.constants';
import { resolveCreatorListLimit } from './creators.limit.utils';
import { normalizeCreatorListSearchTerm } from './creators.search-term.utils';

/**
* Validation schema for creator list query parameters.
Expand All @@ -22,7 +30,7 @@ import { DEFAULT_CREATOR_LIST_SORT } from '../../constants/creator-list-sort.con
export const CreatorListQuerySchema = z.object({
// Pagination
limit: safeIntParam({
defaultValue: PUBLIC_OFFSET_PAGINATION_DEFAULTS.limit,
defaultValue: resolveCreatorListLimit() ?? PUBLIC_OFFSET_PAGINATION_DEFAULTS.limit,
min: MIN_PAGE_SIZE,
max: MAX_PAGE_SIZE,
label: 'Limit',
Expand Down Expand Up @@ -50,7 +58,12 @@ export const CreatorListQuerySchema = z.object({
return val === 'true';
})
),
search: withCreatorListQueryStringNormalization(z.string().optional()),
search: withCreatorListQueryStringNormalization(
z
.string()
.optional()
.transform(val => normalizeCreatorListSearchTerm(val))
),
});

export type CreatorListQueryType = z.infer<typeof CreatorListQuerySchema>;
20 changes: 20 additions & 0 deletions src/modules/creators/creators.search-term.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Normalize free-text creator list search terms before filtering.
*
* Scope is intentionally narrow and reusable:
* - Accepts `string | null | undefined`
* - Trims leading/trailing whitespace
* - Collapses internal whitespace to single spaces
* - Returns `undefined` for empty/whitespace-only values
*/
export function normalizeCreatorListSearchTerm(
term: string | null | undefined
): string | undefined {
if (typeof term !== 'string') {
return undefined;
}

const normalized = term.trim().replace(/\s+/g, ' ');
return normalized === '' ? undefined : normalized;
}

9 changes: 6 additions & 3 deletions src/modules/creators/creators.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { mapCreatorListSort } from './creators.sort';
import { CreatorListResponse } from './creators.serializers';
import { wrapPublicCreatorListResponse } from './public-creator-list-envelope.utils';
import { buildOffsetPaginationMeta } from '../../utils/pagination.utils';
import { normalizeCreatorListSearchTerm } from './creators.search-term.utils';

type CreatorListWhere = {
isVerified?: boolean;
Expand Down Expand Up @@ -32,10 +33,12 @@ export async function fetchCreatorList(
where.isVerified = verified;
}

if (search) {
const normalizedSearch = normalizeCreatorListSearchTerm(search);

if (normalizedSearch) {
where.OR = [
{ handle: { contains: search, mode: 'insensitive' } },
{ displayName: { contains: search, mode: 'insensitive' } },
{ handle: { contains: normalizedSearch, mode: 'insensitive' } },
{ displayName: { contains: normalizedSearch, mode: 'insensitive' } },
];
}

Expand Down
Loading