Add comprehensive edge-function error handling

Enhance error handling and logging across all edge functions:
- Introduce a shared edgeFunctionWrapper with standardized error handling, request/response logging, tracing, and validation hooks.
- Add runtime type validation utilities (ValidationError, validators, and parse/validate helpers) and integrate into edge flow.
- Implement robust validation for incoming requests and known type mismatches, with detailed logs and structured responses.
- Add post-RPC and post-database error logging to surface type/mismatch issues early.
- Update approval/rejection entry points to leverage new validators and centralized error handling.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-11 02:54:50 +00:00
parent 1a101b4109
commit 7181fdbcac
6 changed files with 1333 additions and 62 deletions

View File

@@ -14,6 +14,14 @@ import {
type Span
} from '../_shared/logger.ts';
import { formatEdgeError, toError } from '../_shared/errorFormatter.ts';
import {
validateApprovalRequest,
validateSubmissionItems,
getSubmissionTableName,
getMainTableName,
type ValidatedSubmissionItem
} from '../_shared/submissionValidation.ts';
import { ValidationError } from '../_shared/typeValidation.ts';
const SUPABASE_URL = Deno.env.get('SUPABASE_URL') || 'https://api.thrillwiki.com';
const SUPABASE_ANON_KEY = Deno.env.get('SUPABASE_ANON_KEY');
@@ -140,10 +148,48 @@ const handler = async (req: Request) => {
action: 'process_approval'
});
// STEP 2: Parse request
// STEP 2: Parse and validate request
addSpanEvent(rootSpan, 'validation_start');
const body: ApprovalRequest = await req.json();
const { submissionId, itemIds } = body;
let submissionId: string;
let itemIds: string[];
try {
const body = await req.json();
const validated = validateApprovalRequest(body, requestId);
submissionId = validated.submissionId;
itemIds = validated.itemIds;
} catch (error) {
if (error instanceof ValidationError) {
addSpanEvent(rootSpan, 'validation_failed', {
field: error.field,
expected: error.expected,
received: error.received,
});
edgeLogger.warn('Request validation failed', {
requestId,
field: error.field,
expected: error.expected,
received: error.received,
action: 'process_approval'
});
endSpan(rootSpan, 'error', error);
logSpan(rootSpan);
return new Response(
JSON.stringify({
error: error.message,
field: error.field,
requestId
}),
{
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
}
);
}
throw error;
}
const idempotencyKey = req.headers.get('x-idempotency-key');
if (!idempotencyKey) {
@@ -160,33 +206,6 @@ const handler = async (req: Request) => {
);
}
if (!submissionId || !itemIds || itemIds.length === 0) {
addSpanEvent(rootSpan, 'validation_failed', {
hasSubmissionId: !!submissionId,
hasItemIds: !!itemIds,
itemCount: itemIds?.length || 0,
});
edgeLogger.warn('Invalid request payload', {
requestId,
hasSubmissionId: !!submissionId,
hasItemIds: !!itemIds,
itemCount: itemIds?.length || 0,
action: 'process_approval'
});
endSpan(rootSpan, 'error');
logSpan(rootSpan);
return new Response(
JSON.stringify({ error: 'Missing required fields: submissionId, itemIds' }),
{
status: 400,
headers: {
...corsHeaders,
'Content-Type': 'application/json'
}
}
);
}
setSpanAttributes(rootSpan, {
'submission.id': submissionId,
'submission.item_count': itemIds.length,
@@ -422,6 +441,25 @@ const handler = async (req: Request) => {
error: rpcError.message,
errorCode: rpcError.code
});
// Enhanced error logging for type mismatches
if (rpcError.code === 'P0001' && rpcError.message?.includes('Unknown item type')) {
// Extract the unknown type from error message
const typeMatch = rpcError.message.match(/Unknown item type: (\w+)/);
const unknownType = typeMatch ? typeMatch[1] : 'unknown';
edgeLogger.error('Entity type mismatch detected', {
requestId,
submissionId,
unknownType,
error: rpcError.message,
hint: `The submission contains an item with type '${unknownType}' which is not recognized by process_approval_transaction. ` +
`Valid types are: park, ride, manufacturer, operator, property_owner, designer, company, ride_model, photo. ` +
`This indicates a data model inconsistency between submission_items and the RPC function.`,
action: 'process_approval'
});
}
break;
}