feat: Add idempotency to useModerationActions

Implement idempotency integration in the useModerationActions hook as per the detailed plan.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-06 17:43:16 +00:00
parent 85436b5c1e
commit b92a62ebc8
3 changed files with 321 additions and 30 deletions

View File

@@ -19,7 +19,10 @@ import { breadcrumb } from './errorBreadcrumbs';
* @param userId - User ID for tracking (optional)
* @param parentRequestId - Parent request ID for chaining (optional)
* @param traceId - Trace ID for distributed tracing (optional)
* @returns Response data with requestId
* @param timeout - Request timeout in milliseconds (default: 30000)
* @param retryOptions - Optional retry configuration
* @param customHeaders - Custom headers to include in the request (e.g., X-Idempotency-Key)
* @returns Response data with requestId, status, and tracking info
*/
export async function invokeWithTracking<T = any>(
functionName: string,
@@ -27,9 +30,10 @@ export async function invokeWithTracking<T = any>(
userId?: string,
parentRequestId?: string,
traceId?: string,
timeout: number = 30000, // Default 30s timeout
retryOptions?: Partial<RetryOptions> // NEW: Optional retry configuration
): Promise<{ data: T | null; error: any; requestId: string; duration: number; attempts?: number }> {
timeout: number = 30000,
retryOptions?: Partial<RetryOptions>,
customHeaders?: Record<string, string>
): Promise<{ data: T | null; error: any; requestId: string; duration: number; attempts?: number; status?: number }> {
// Configure retry options with defaults
const effectiveRetryOptions: RetryOptions = {
maxAttempts: retryOptions?.maxAttempts ?? 3,
@@ -75,14 +79,16 @@ export async function invokeWithTracking<T = any>(
const { data, error } = await supabase.functions.invoke<T>(functionName, {
body: { ...payload, clientRequestId: context.requestId },
signal: controller.signal,
headers: customHeaders,
});
clearTimeout(timeoutId);
if (error) {
// Enhance error with status for retry logic
// Enhance error with status and context for retry logic
const enhancedError = new Error(error.message || 'Edge function error');
(enhancedError as any).status = error.status;
(enhancedError as any).context = error.context;
throw enhancedError;
}
@@ -97,7 +103,7 @@ export async function invokeWithTracking<T = any>(
}
);
return { data: result, error: null, requestId, duration, attempts: attemptCount };
return { data: result, error: null, requestId, duration, attempts: attemptCount, status: 200 };
} catch (error: unknown) {
// Handle AbortError specifically
if (error instanceof Error && error.name === 'AbortError') {
@@ -110,16 +116,18 @@ export async function invokeWithTracking<T = any>(
requestId: 'timeout',
duration: timeout,
attempts: attemptCount,
status: 408,
};
}
const errorMessage = getErrorMessage(error);
return {
data: null,
error: { message: errorMessage },
error: { message: errorMessage, status: (error as any)?.status },
requestId: 'unknown',
duration: 0,
attempts: attemptCount,
status: (error as any)?.status,
};
}
}
@@ -148,6 +156,7 @@ export async function invokeBatchWithTracking<T = any>(
requestId: string;
duration: number;
attempts?: number;
status?: number;
}>
> {
const traceId = crypto.randomUUID();
@@ -160,8 +169,8 @@ export async function invokeBatchWithTracking<T = any>(
userId,
undefined,
traceId,
30000, // default timeout
op.retryOptions // Pass through retry options
30000,
op.retryOptions
);
return { functionName: op.functionName, ...result };
})