mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:31:11 -05:00
Fix: Improve validation error handling
This commit is contained in:
@@ -837,107 +837,108 @@ serve(withRateLimit(async (req) => {
|
|||||||
|
|
||||||
// Process items in order
|
// Process items in order
|
||||||
for (const item of sortedItems) {
|
for (const item of sortedItems) {
|
||||||
try {
|
edgeLogger.info('Processing item', { action: 'approval_process_item', itemId: item.id, itemType: item.item_type });
|
||||||
edgeLogger.info('Processing item', { action: 'approval_process_item', itemId: item.id, itemType: item.item_type });
|
|
||||||
|
// Extract data from relational tables based on item_type (OUTSIDE try-catch)
|
||||||
// Extract data from relational tables based on item_type
|
let itemData: any;
|
||||||
let itemData: any;
|
switch (item.item_type) {
|
||||||
switch (item.item_type) {
|
case 'park':
|
||||||
case 'park':
|
itemData = {
|
||||||
itemData = {
|
...(item as any).park_submission,
|
||||||
...(item as any).park_submission,
|
// Merge temp refs for this item
|
||||||
// Merge temp refs for this item
|
...(tempRefsByItemId.get(item.id) || {})
|
||||||
...(tempRefsByItemId.get(item.id) || {})
|
};
|
||||||
};
|
break;
|
||||||
break;
|
case 'ride':
|
||||||
case 'ride':
|
itemData = {
|
||||||
itemData = {
|
...(item as any).ride_submission,
|
||||||
...(item as any).ride_submission,
|
...(tempRefsByItemId.get(item.id) || {})
|
||||||
...(tempRefsByItemId.get(item.id) || {})
|
};
|
||||||
};
|
break;
|
||||||
break;
|
case 'manufacturer':
|
||||||
case 'manufacturer':
|
case 'operator':
|
||||||
case 'operator':
|
case 'property_owner':
|
||||||
case 'property_owner':
|
case 'designer':
|
||||||
case 'designer':
|
itemData = {
|
||||||
itemData = {
|
...(item as any).company_submission,
|
||||||
...(item as any).company_submission,
|
...(tempRefsByItemId.get(item.id) || {})
|
||||||
...(tempRefsByItemId.get(item.id) || {})
|
};
|
||||||
};
|
break;
|
||||||
break;
|
case 'ride_model':
|
||||||
case 'ride_model':
|
itemData = {
|
||||||
itemData = {
|
...(item as any).ride_model_submission,
|
||||||
...(item as any).ride_model_submission,
|
...(tempRefsByItemId.get(item.id) || {})
|
||||||
...(tempRefsByItemId.get(item.id) || {})
|
};
|
||||||
};
|
break;
|
||||||
break;
|
case 'photo':
|
||||||
case 'photo':
|
// Combine photo_submission with its photo_items array
|
||||||
// Combine photo_submission with its photo_items array
|
itemData = {
|
||||||
itemData = {
|
...(item as any).photo_submission,
|
||||||
...(item as any).photo_submission,
|
photos: (item as any).photo_submission?.photo_items || [],
|
||||||
photos: (item as any).photo_submission?.photo_items || [],
|
...(tempRefsByItemId.get(item.id) || {})
|
||||||
...(tempRefsByItemId.get(item.id) || {})
|
};
|
||||||
};
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
// For timeline/other items not yet migrated, fall back to item_data (JSONB)
|
||||||
// For timeline/other items not yet migrated, fall back to item_data (JSONB)
|
|
||||||
itemData = item.item_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!itemData && item.item_data) {
|
|
||||||
// Fallback to item_data if relational data not found (for backwards compatibility)
|
|
||||||
itemData = item.item_data;
|
itemData = item.item_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!itemData && item.item_data) {
|
||||||
|
// Fallback to item_data if relational data not found (for backwards compatibility)
|
||||||
|
itemData = item.item_data;
|
||||||
|
}
|
||||||
|
|
||||||
// Log if temp refs were found for this item
|
// Log if temp refs were found for this item
|
||||||
if (tempRefsByItemId.has(item.id)) {
|
if (tempRefsByItemId.has(item.id)) {
|
||||||
edgeLogger.info('Item has temp refs', {
|
edgeLogger.info('Item has temp refs', {
|
||||||
action: 'approval_item_temp_refs',
|
action: 'approval_item_temp_refs',
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
itemType: item.item_type,
|
itemType: item.item_type,
|
||||||
tempRefs: tempRefsByItemId.get(item.id),
|
tempRefs: tempRefsByItemId.get(item.id),
|
||||||
requestId: tracking.requestId
|
requestId: tracking.requestId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate entity data with strict validation, passing original_data for edits
|
// Validate entity data BEFORE entering try-catch (so 400 returns immediately)
|
||||||
const validation = validateEntityDataStrict(item.item_type, itemData, item.original_data);
|
const validation = validateEntityDataStrict(item.item_type, itemData, item.original_data);
|
||||||
|
|
||||||
if (validation.blockingErrors.length > 0) {
|
if (validation.blockingErrors.length > 0) {
|
||||||
edgeLogger.error('Blocking validation errors', {
|
edgeLogger.error('Blocking validation errors', {
|
||||||
action: 'approval_validation_fail',
|
action: 'approval_validation_fail',
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
errors: validation.blockingErrors,
|
errors: validation.blockingErrors,
|
||||||
requestId: tracking.requestId
|
requestId: tracking.requestId
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fail the entire batch if ANY item has blocking errors
|
|
||||||
return new Response(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
message: 'Validation failed: Items have blocking errors that must be fixed',
|
|
||||||
errors: validation.blockingErrors,
|
|
||||||
failedItemId: item.id,
|
|
||||||
failedItemType: item.item_type,
|
|
||||||
requestId: tracking.requestId
|
|
||||||
}), {
|
|
||||||
status: 400,
|
|
||||||
headers: {
|
|
||||||
...corsHeaders,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Request-ID': tracking.requestId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validation.warnings.length > 0) {
|
|
||||||
edgeLogger.warn('Validation warnings', {
|
|
||||||
action: 'approval_validation_warning',
|
|
||||||
itemId: item.id,
|
|
||||||
warnings: validation.warnings
|
|
||||||
});
|
|
||||||
// Continue processing - warnings don't block approval
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Return 400 immediately - NOT caught by try-catch below
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: 'Validation failed: Items have blocking errors that must be fixed',
|
||||||
|
errors: validation.blockingErrors,
|
||||||
|
failedItemId: item.id,
|
||||||
|
failedItemType: item.item_type,
|
||||||
|
requestId: tracking.requestId
|
||||||
|
}), {
|
||||||
|
status: 400,
|
||||||
|
headers: {
|
||||||
|
...corsHeaders,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Request-ID': tracking.requestId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validation.warnings.length > 0) {
|
||||||
|
edgeLogger.warn('Validation warnings', {
|
||||||
|
action: 'approval_validation_warning',
|
||||||
|
itemId: item.id,
|
||||||
|
warnings: validation.warnings
|
||||||
|
});
|
||||||
|
// Continue processing - warnings don't block approval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now enter try-catch ONLY for database operations
|
||||||
|
try {
|
||||||
// Set user context for versioning trigger
|
// Set user context for versioning trigger
|
||||||
// This allows create_relational_version() trigger to capture the submitter
|
// This allows create_relational_version() trigger to capture the submitter
|
||||||
const { error: setUserIdError } = await supabase.rpc('set_config_value', {
|
const { error: setUserIdError } = await supabase.rpc('set_config_value', {
|
||||||
|
|||||||
Reference in New Issue
Block a user