Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
109 changes: 46 additions & 63 deletions src/modules/creator/creator.controller.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,60 @@
// src/modules/creator/creator.controller.ts
import { Request, Response } from 'express';
import { z } from 'zod';
import { RequestHandler } from 'express';

// API response helpers
import {
sendSuccess,
sendError,
sendValidationError,
ErrorCode,
sendSuccess,
sendError,
sendValidationError,
ErrorCode,
} from '../../utils/api-response.utils';

// Creator service and utilities
import { getPaginatedCreators } from './creator.service';
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';

Check failure on line 17 in src/modules/creator/creator.controller.ts

View workflow job for this annotation

GitHub Actions / verify

'resolveCreatorListLimit' is defined but never used. Allowed unused vars must match /^_/u
import { buildCreatorListRequestContext } from '../creators/creator-list-context.utils';
import { normalizeCreatorListPage } from './creator-list-page.guard';
import {
MIN_PAGE_SIZE,
MAX_PAGE_SIZE,
} from '../../constants/pagination.constants';
import { PUBLIC_PAGE_PAGINATION_DEFAULTS } from '../../utils/public-list-query-defaults';

const LegacyCreatorQuerySchema = z.object({
page: safeIntParam({
defaultValue: PUBLIC_PAGE_PAGINATION_DEFAULTS.page,
min: MIN_PAGE_SIZE,
max: Number.MAX_SAFE_INTEGER,
label: 'Page',
}),
limit: safeIntParam({
defaultValue: PUBLIC_PAGE_PAGINATION_DEFAULTS.limit,
min: MIN_PAGE_SIZE,
max: MAX_PAGE_SIZE,
label: 'Limit',
}),
sortBy: z.string().optional(),
sortOrder: z.string().optional(),
});
// Legacy query schema
import { LegacyCreatorQuerySchema } from '../creators/creators.schemas';

// Typed Express handler
export const listCreators: RequestHandler = async (req, res) => {
try {
const ctx = buildCreatorListRequestContext(req);
const parsed = parsePublicQuery(LegacyCreatorQuerySchema, ctx.query);

if (!parsed.ok) {
return sendValidationError(res, 'Invalid query parameters', parsed.details);
}

// Destructure once
let { page, limit, sortBy, sortOrder } = parsed.data;

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 { limit, sortBy, sortOrder } = parsed.data;
const page = normalizeCreatorListPage(parsed.data.page);
// Normalize page
page = normalizeCreatorListPage(page);

const sort = parseCreatorSortOptions(sortBy, sortOrder);
// Build sort options
const sort = parseCreatorSortOptions(sortBy, sortOrder);

const { creators, meta } = await getPaginatedCreators({
page,
limit,
sort,
});
// Fetch paginated creators
const { creators, meta } = await getPaginatedCreators({
page,
limit,
sort,
});

return sendSuccess(
res,
wrapPublicCreatorListResponse(creators, meta),
200,
'Creators retrieved successfully'
);
} catch (error) {
console.error('Error listing creators:', error);
return sendError(
res,
500,
ErrorCode.INTERNAL_ERROR,
'Failed to retrieve creators'
);
}
}
return sendSuccess(
res,
wrapPublicCreatorListResponse(creators, meta),
200,
'Creators retrieved successfully'
);
} catch (error) {
console.error('Error listing creators:', error);
return sendError(res, 500, ErrorCode.INTERNAL_ERROR, 'Failed to retrieve creators');
}
};
83 changes: 41 additions & 42 deletions src/modules/creators/creators.schemas.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
// src/modules/creators/creators.schemas.ts
import { z } from 'zod';
import { creatorListSortDirectionQueryParam } from './creators.sort-direction.parse';
import { creatorListIncludeQueryParam } from './creators.include.parse';
import { withCreatorListQueryStringNormalization } from './creators.query-string.utils';
import { safeIntParam } from '../../utils/query.utils';
import {
MIN_PAGE_SIZE,
MAX_PAGE_SIZE,
MIN_PAGE_SIZE,
MAX_PAGE_SIZE,
} from '../../constants/pagination.constants';
import { PUBLIC_OFFSET_PAGINATION_DEFAULTS } from '../../utils/public-list-query-defaults';

import {
CREATOR_LIST_SORT_FIELDS,
DEFAULT_CREATOR_LIST_SORT,
CREATOR_LIST_SORT_FIELDS,
DEFAULT_CREATOR_LIST_SORT,
} from '../../constants/creator-list-sort.constants';
import { resolveCreatorListLimit } from './creators.limit.utils';
import { normalizeCreatorListSearchTerm } from './creators.search-term.utils';

/**
Expand All @@ -25,45 +26,43 @@ import { normalizeCreatorListSearchTerm } from './creators.search-term.utils';
* GET /api/v1/creators?limit=20&offset=0&sort=createdAt&order=desc&verified=true
*/
export const CreatorListQuerySchema = z.object({
// Pagination
limit: safeIntParam({
defaultValue: PUBLIC_OFFSET_PAGINATION_DEFAULTS.limit,
min: MIN_PAGE_SIZE,
max: MAX_PAGE_SIZE,
label: 'Limit',
}),
offset: safeIntParam({
defaultValue: PUBLIC_OFFSET_PAGINATION_DEFAULTS.offset,
min: 0,
max: Number.MAX_SAFE_INTEGER,
label: 'Offset',
}),
// Pagination
limit: safeIntParam({
defaultValue: resolveCreatorListLimit() ?? PUBLIC_OFFSET_PAGINATION_DEFAULTS.limit,
min: MIN_PAGE_SIZE,
max: MAX_PAGE_SIZE,
label: 'Limit',
}),
offset: safeIntParam({
defaultValue: PUBLIC_OFFSET_PAGINATION_DEFAULTS.offset,
min: 0,
max: Number.MAX_SAFE_INTEGER,
label: 'Offset',
}),

// Sorting
sort: withCreatorListQueryStringNormalization(
z.enum(CREATOR_LIST_SORT_FIELDS)
.optional()
.default(DEFAULT_CREATOR_LIST_SORT)
),
order: creatorListSortDirectionQueryParam(),
include: creatorListIncludeQueryParam(),
// Sorting
sort: withCreatorListQueryStringNormalization(
z.enum(CREATOR_LIST_SORT_FIELDS).optional().default(DEFAULT_CREATOR_LIST_SORT)
),
order: creatorListSortDirectionQueryParam(),
include: creatorListIncludeQueryParam(),

// Filters
verified: withCreatorListQueryStringNormalization(
z
.string()
.optional()
.transform(val => {
if (val === undefined) return undefined;
return val === 'true';
})
),
search: withCreatorListQueryStringNormalization(
z
.string()
.optional()
.transform(val => normalizeCreatorListSearchTerm(val))
),
// Filters
verified: withCreatorListQueryStringNormalization(
z
.string()
.optional()
.transform(val => (val === undefined ? undefined : val === 'true'))
),
search: withCreatorListQueryStringNormalization(
z
.string()
.optional()
.transform(val => normalizeCreatorListSearchTerm(val))
),
});

// Export as LegacyCreatorQuerySchema for backward compatibility
export const LegacyCreatorQuerySchema = CreatorListQuerySchema;

export type CreatorListQueryType = z.infer<typeof CreatorListQuerySchema>;
Loading