mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 07:31:12 -05:00
Fix composite submission error handling
This commit is contained in:
@@ -347,6 +347,29 @@ async function submitCompositeCreation(
|
||||
depends_on: null // Will be set by RPC based on refs
|
||||
});
|
||||
|
||||
// Pre-validation to catch issues early with actionable error messages
|
||||
if (uploadedPrimary.type === 'park') {
|
||||
if (!primaryData.name) throw new Error('Park name is required');
|
||||
if (!primaryData.slug) throw new Error('Park slug is required');
|
||||
if (!primaryData.park_type) throw new Error('Park type is required');
|
||||
if (!primaryData.status) throw new Error('Park status is required');
|
||||
} else if (uploadedPrimary.type === 'ride') {
|
||||
if (!primaryData.name) throw new Error('Ride name is required');
|
||||
if (!primaryData.slug) throw new Error('Ride slug is required');
|
||||
if (!primaryData.status) throw new Error('Ride status is required');
|
||||
}
|
||||
|
||||
// Validate dependencies
|
||||
for (const dep of uploadedDependencies) {
|
||||
if (dep.type === 'company') {
|
||||
if (!dep.data.name) throw new Error(`${dep.companyType || 'Company'} name is required`);
|
||||
if (!dep.data.slug) throw new Error(`${dep.companyType || 'Company'} slug is required`);
|
||||
if (!dep.data.company_type && !dep.companyType) {
|
||||
throw new Error('Company type is required');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use RPC to create submission with items atomically
|
||||
const { data: result, error } = await supabase.rpc('create_submission_with_items', {
|
||||
p_user_id: userId,
|
||||
@@ -356,11 +379,30 @@ async function submitCompositeCreation(
|
||||
});
|
||||
|
||||
if (error) {
|
||||
handleError(error, {
|
||||
// Extract Supabase error details for better error logging
|
||||
const supabaseError = error as { message?: string; code?: string; details?: string; hint?: string };
|
||||
const errorMessage = supabaseError.message || 'Unknown error';
|
||||
const errorCode = supabaseError.code;
|
||||
const errorDetails = supabaseError.details;
|
||||
const errorHint = supabaseError.hint;
|
||||
|
||||
// Create proper Error instance with enhanced context
|
||||
const enhancedError = new Error(
|
||||
`Composite submission failed: ${errorMessage}${errorDetails ? `\nDetails: ${errorDetails}` : ''}${errorHint ? `\nHint: ${errorHint}` : ''}`
|
||||
);
|
||||
|
||||
handleError(enhancedError, {
|
||||
action: 'Composite submission',
|
||||
metadata: { primaryType: uploadedPrimary.type, dependencyCount: dependencies.length },
|
||||
metadata: {
|
||||
primaryType: uploadedPrimary.type,
|
||||
dependencyCount: dependencies.length,
|
||||
supabaseCode: errorCode,
|
||||
supabaseDetails: errorDetails,
|
||||
supabaseHint: errorHint
|
||||
},
|
||||
});
|
||||
throw new Error(`Failed to create composite submission: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
|
||||
throw enhancedError;
|
||||
}
|
||||
|
||||
return { submitted: true, submissionId: result };
|
||||
|
||||
@@ -30,23 +30,60 @@ export const handleError = (
|
||||
const errorId = (context.metadata?.requestId as string) || crypto.randomUUID();
|
||||
const shortErrorId = errorId.slice(0, 8);
|
||||
|
||||
const errorMessage = error instanceof AppError
|
||||
? error.userMessage || error.message
|
||||
: error instanceof Error
|
||||
? error.message
|
||||
: 'An unexpected error occurred';
|
||||
// Enhanced error message and stack extraction
|
||||
let errorMessage: string;
|
||||
let stack: string | undefined;
|
||||
let errorName = 'UnknownError';
|
||||
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error instanceof AppError
|
||||
? error.userMessage || error.message
|
||||
: error.message;
|
||||
stack = error.stack;
|
||||
errorName = error.name;
|
||||
} else if (error && typeof error === 'object') {
|
||||
// Handle Supabase errors (plain objects with message/code/details)
|
||||
const supabaseError = error as {
|
||||
message?: string;
|
||||
code?: string;
|
||||
details?: string;
|
||||
hint?: string;
|
||||
};
|
||||
|
||||
errorMessage = supabaseError.message || 'An unexpected error occurred';
|
||||
errorName = 'SupabaseError';
|
||||
|
||||
// Create synthetic stack trace for Supabase errors to aid debugging
|
||||
if (supabaseError.code || supabaseError.details || supabaseError.hint) {
|
||||
const stackParts = [
|
||||
`SupabaseError: ${errorMessage}`,
|
||||
supabaseError.code ? ` Code: ${supabaseError.code}` : null,
|
||||
supabaseError.details ? ` Details: ${supabaseError.details}` : null,
|
||||
supabaseError.hint ? ` Hint: ${supabaseError.hint}` : null,
|
||||
` at ${context.action}`,
|
||||
` Reference ID: ${errorId}`
|
||||
].filter(Boolean);
|
||||
|
||||
stack = stackParts.join('\n');
|
||||
}
|
||||
} else if (typeof error === 'string') {
|
||||
errorMessage = error;
|
||||
} else {
|
||||
errorMessage = 'An unexpected error occurred';
|
||||
}
|
||||
|
||||
// Log to console/monitoring with enhanced debugging
|
||||
const stack = error instanceof Error ? error.stack : undefined;
|
||||
|
||||
logger.error('Error occurred', {
|
||||
...context,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
error: errorMessage,
|
||||
stack,
|
||||
errorId,
|
||||
errorName,
|
||||
errorType: typeof error,
|
||||
errorConstructor: error?.constructor?.name,
|
||||
hasStack: !!stack,
|
||||
isSyntheticStack: !!(error && typeof error === 'object' && !(error instanceof Error) && stack),
|
||||
});
|
||||
|
||||
// Additional debug logging when stack is missing
|
||||
@@ -72,9 +109,9 @@ export const handleError = (
|
||||
p_endpoint: context.action,
|
||||
p_method: 'ERROR',
|
||||
p_status_code: 500,
|
||||
p_error_type: error instanceof Error ? error.name : 'UnknownError',
|
||||
p_error_type: errorName,
|
||||
p_error_message: errorMessage,
|
||||
p_error_stack: error instanceof Error ? error.stack : undefined,
|
||||
p_error_stack: stack,
|
||||
p_user_agent: navigator.userAgent,
|
||||
p_breadcrumbs: JSON.stringify(breadcrumbs),
|
||||
p_timezone: envContext.timezone,
|
||||
|
||||
Reference in New Issue
Block a user