/** * Submission-Specific Validation Utilities * * Validates submission and moderation request structures * Ensures type safety across the submission pipeline */ import { validateUUID, validateUUIDArray, validateEntityType, validateActionType, validateObject, validateString, validateArray, type ValidEntityType, type ValidActionType, } from './typeValidation.ts'; /** * Validated approval request structure */ export interface ValidatedApprovalRequest { submissionId: string; itemIds: string[]; } /** * Validated rejection request structure */ export interface ValidatedRejectionRequest { submissionId: string; itemIds: string[]; rejectionReason: string; } /** * Validated submission item */ export interface ValidatedSubmissionItem { id: string; item_type: ValidEntityType; action_type: ValidActionType; entity_id?: string | null; item_data?: Record; } /** * Validate approval request body */ export function validateApprovalRequest( body: unknown, requestId?: string ): ValidatedApprovalRequest { validateObject(body, 'request_body', { requestId }); const obj = body as Record; validateUUID(obj.submissionId, 'submissionId', { requestId }); validateUUIDArray(obj.itemIds, 'itemIds', 1, { requestId }); return { submissionId: obj.submissionId as string, itemIds: obj.itemIds as string[], }; } /** * Validate rejection request body */ export function validateRejectionRequest( body: unknown, requestId?: string ): ValidatedRejectionRequest { validateObject(body, 'request_body', { requestId }); const obj = body as Record; validateUUID(obj.submissionId, 'submissionId', { requestId }); validateUUIDArray(obj.itemIds, 'itemIds', 1, { requestId }); validateString(obj.rejectionReason, 'rejectionReason', { requestId }); return { submissionId: obj.submissionId as string, itemIds: obj.itemIds as string[], rejectionReason: obj.rejectionReason as string, }; } /** * Validate submission item from database */ export function validateSubmissionItemFromDB( item: unknown, context?: Record ): ValidatedSubmissionItem { validateObject(item, 'submission_item', context); const obj = item as Record; // Validate required fields validateUUID(obj.id, 'submission_item.id', context); validateEntityType(obj.item_type, 'submission_item.item_type', { ...context, itemId: obj.id, }); validateActionType(obj.action_type, 'submission_item.action_type', { ...context, itemId: obj.id, }); return { id: obj.id as string, item_type: obj.item_type as ValidEntityType, action_type: obj.action_type as ValidActionType, entity_id: obj.entity_id as string | null | undefined, item_data: obj.item_data as Record | undefined, }; } /** * Validate array of submission items */ export function validateSubmissionItems( items: unknown, context?: Record ): ValidatedSubmissionItem[] { validateArray(items, 'submission_items', 1, context); const itemArray = items as unknown[]; return itemArray.map((item, index) => validateSubmissionItemFromDB(item, { ...context, itemIndex: index, }) ); } /** * Validate that entity type matches the expected submission table * Helps catch data model mismatches early */ export function validateEntityTypeConsistency( item: ValidatedSubmissionItem, expectedTypes: ValidEntityType[], context?: Record ): void { if (!expectedTypes.includes(item.item_type)) { throw new Error( `Entity type mismatch: expected one of [${expectedTypes.join(', ')}] but got '${item.item_type}' ` + `for item ${item.id}. This may indicate a data model inconsistency. ` + `Context: ${JSON.stringify(context)}` ); } } /** * Map entity type to submission table name * Useful for debugging and error messages */ export function getSubmissionTableName(entityType: ValidEntityType): string { const tableMap: Record = { park: 'park_submissions', ride: 'ride_submissions', manufacturer: 'company_submissions', operator: 'company_submissions', property_owner: 'company_submissions', designer: 'company_submissions', company: 'company_submissions', ride_model: 'ride_model_submissions', photo: 'photo_submissions', milestone: 'timeline_event_submissions', timeline_event: 'timeline_event_submissions', }; return tableMap[entityType] || 'unknown_submissions'; } /** * Map entity type to main table name * Useful for debugging and error messages */ export function getMainTableName(entityType: ValidEntityType): string { const tableMap: Record = { park: 'parks', ride: 'rides', manufacturer: 'companies', operator: 'companies', property_owner: 'companies', designer: 'companies', company: 'companies', ride_model: 'ride_models', photo: 'photos', milestone: 'timeline_events', timeline_event: 'timeline_events', }; return tableMap[entityType] || 'unknown_table'; }