mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 03:51: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
|
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
|
// Use RPC to create submission with items atomically
|
||||||
const { data: result, error } = await supabase.rpc('create_submission_with_items', {
|
const { data: result, error } = await supabase.rpc('create_submission_with_items', {
|
||||||
p_user_id: userId,
|
p_user_id: userId,
|
||||||
@@ -356,11 +379,30 @@ async function submitCompositeCreation(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
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',
|
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 };
|
return { submitted: true, submissionId: result };
|
||||||
|
|||||||
@@ -30,23 +30,60 @@ export const handleError = (
|
|||||||
const errorId = (context.metadata?.requestId as string) || crypto.randomUUID();
|
const errorId = (context.metadata?.requestId as string) || crypto.randomUUID();
|
||||||
const shortErrorId = errorId.slice(0, 8);
|
const shortErrorId = errorId.slice(0, 8);
|
||||||
|
|
||||||
const errorMessage = error instanceof AppError
|
// Enhanced error message and stack extraction
|
||||||
? error.userMessage || error.message
|
let errorMessage: string;
|
||||||
: error instanceof Error
|
let stack: string | undefined;
|
||||||
? error.message
|
let errorName = 'UnknownError';
|
||||||
: 'An unexpected error occurred';
|
|
||||||
|
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
|
// Log to console/monitoring with enhanced debugging
|
||||||
const stack = error instanceof Error ? error.stack : undefined;
|
|
||||||
|
|
||||||
logger.error('Error occurred', {
|
logger.error('Error occurred', {
|
||||||
...context,
|
...context,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: errorMessage,
|
||||||
stack,
|
stack,
|
||||||
errorId,
|
errorId,
|
||||||
|
errorName,
|
||||||
errorType: typeof error,
|
errorType: typeof error,
|
||||||
errorConstructor: error?.constructor?.name,
|
errorConstructor: error?.constructor?.name,
|
||||||
hasStack: !!stack,
|
hasStack: !!stack,
|
||||||
|
isSyntheticStack: !!(error && typeof error === 'object' && !(error instanceof Error) && stack),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Additional debug logging when stack is missing
|
// Additional debug logging when stack is missing
|
||||||
@@ -72,9 +109,9 @@ export const handleError = (
|
|||||||
p_endpoint: context.action,
|
p_endpoint: context.action,
|
||||||
p_method: 'ERROR',
|
p_method: 'ERROR',
|
||||||
p_status_code: 500,
|
p_status_code: 500,
|
||||||
p_error_type: error instanceof Error ? error.name : 'UnknownError',
|
p_error_type: errorName,
|
||||||
p_error_message: errorMessage,
|
p_error_message: errorMessage,
|
||||||
p_error_stack: error instanceof Error ? error.stack : undefined,
|
p_error_stack: stack,
|
||||||
p_user_agent: navigator.userAgent,
|
p_user_agent: navigator.userAgent,
|
||||||
p_breadcrumbs: JSON.stringify(breadcrumbs),
|
p_breadcrumbs: JSON.stringify(breadcrumbs),
|
||||||
p_timezone: envContext.timezone,
|
p_timezone: envContext.timezone,
|
||||||
|
|||||||
Reference in New Issue
Block a user