UNPKG

deepsource-mcp-server

Version:
240 lines 9.41 kB
/** * @fileoverview Error handlers for specific error types * This module provides utilities for handling different types of errors. */ import { ErrorCategory } from './categories.js'; import { createClassifiedError } from './factory.js'; import { createLogger } from '../logging/logger.js'; /** * Error classifier for GraphQL errors * @param error The error to classify * @returns The classified error category * @public */ export function classifyGraphQLError(error) { const message = error.message.toLowerCase(); // Define error patterns and their corresponding categories const errorPatterns = { [ErrorCategory.AUTH]: [ 'authentication', 'unauthorized', 'access denied', 'not authorized', 'forbidden', 'token', 'api key', ], [ErrorCategory.RATE_LIMIT]: ['rate limit', 'too many requests', 'throttled'], [ErrorCategory.NETWORK]: ['network', 'connection', 'econnreset', 'econnrefused'], [ErrorCategory.TIMEOUT]: ['timeout', 'timed out', 'etimedout'], [ErrorCategory.SCHEMA]: [ 'cannot query field', 'unknown argument', 'unknown type', 'field not defined', ], [ErrorCategory.NOT_FOUND]: ['not found', 'nonetype', 'does not exist'], [ErrorCategory.SERVER]: ['server error', 'internal error', '500'], [ErrorCategory.CLIENT]: [], [ErrorCategory.FORMAT]: [], [ErrorCategory.OTHER]: [], }; // Check each category's patterns for (const [category, patterns] of Object.entries(errorPatterns)) { if (patterns.some((pattern) => message.includes(pattern))) { return category; } } // Default to OTHER return ErrorCategory.OTHER; } /** * Type guard to check if an unknown error is an Error object * @param error The error to check * @returns True if the error is an Error instance * @public */ export function isError(error) { return (error !== null && typeof error === 'object' && 'message' in error && typeof error.message === 'string'); } /** * Type guard to check if an error contains a specific message substring * @param error The error to check * @param substring The substring to search for in the error message * @returns True if the error is an Error with the specified substring * @public */ export function isErrorWithMessage(error, substring) { return isError(error) && error.message?.includes(substring); } /** * Checks if an error is an Axios error with specific characteristics * @param error The error to check * @param statusCode Optional HTTP status code to match * @param errorCode Optional Axios error code to match * @returns True if the error matches the criteria and is an AxiosError, false otherwise * @public */ export function isAxiosErrorWithCriteria(error, statusCode, errorCode) { // Check if it's an object first if (!error || typeof error !== 'object') { return false; } // Check if it has the axios error shape and matches all criteria const potentialAxiosError = error; return (Boolean(potentialAxiosError.isAxiosError) && (statusCode === undefined || potentialAxiosError.response?.status === statusCode) && (errorCode === undefined || potentialAxiosError.code === errorCode)); } /** * Extract error messages from GraphQL error response * @param errors Array of GraphQL error objects * @returns Formatted error message string * @public */ export function extractGraphQLErrorMessages(errors) { const errorMessages = errors.map((error) => error.message); return errorMessages.join(', '); } /** * Handle GraphQL-specific errors from Axios responses * @param error The error to check for GraphQL errors * @returns A ClassifiedError if a GraphQL error is found, otherwise null * @public */ export function handleGraphQLSpecificError(error) { if (isAxiosErrorWithCriteria(error) && typeof error.response?.data === 'object' && error.response.data && 'errors' in error.response.data) { const graphqlErrors = error.response.data.errors; const errorMessage = extractGraphQLErrorMessages(graphqlErrors); // Create a combined error message const combinedError = new Error(`GraphQL Error: ${errorMessage}`); // Classify the error const category = classifyGraphQLError(combinedError); return createClassifiedError(combinedError.message, category, error, { graphqlErrors }); } return null; } /** * Handle network and connection errors * @param error The error to check * @returns A ClassifiedError if a network error is found, otherwise null * @public */ export function handleNetworkError(error) { if (!isAxiosErrorWithCriteria(error)) { return null; } // Define a lookup table for error codes const errorCodeHandlers = { ECONNREFUSED: () => createClassifiedError('Connection error: Unable to connect to DeepSource API', ErrorCategory.NETWORK, error), ETIMEDOUT: () => createClassifiedError('Timeout error: DeepSource API request timed out', ErrorCategory.TIMEOUT, error), }; // Get the error code const errorCode = error.code; // Return the appropriate classified error based on the error code return errorCode && errorCodeHandlers[errorCode] ? errorCodeHandlers[errorCode]() : null; } /** * Handle HTTP status-specific errors * @param error The error to check * @returns A ClassifiedError if an HTTP status error is found, otherwise null * @public */ export function handleHttpStatusError(error) { if (!isAxiosErrorWithCriteria(error)) { return null; } const status = error.response?.status; if (!status) { return null; } // Define a lookup table for specific HTTP status codes const statusHandlers = { 401: () => createClassifiedError('Authentication error: Invalid or expired API key', ErrorCategory.AUTH, error), 429: () => createClassifiedError('Rate limit exceeded: Too many requests to DeepSource API', ErrorCategory.RATE_LIMIT, error), 404: () => createClassifiedError('Not found (404): The requested resource was not found', ErrorCategory.NOT_FOUND, error), }; // Check for exact status code matches first if (statusHandlers[status]) { return statusHandlers[status](); } // Handle status code ranges if (status >= 500) { return createClassifiedError(`Server error (${status}): DeepSource API server error`, ErrorCategory.SERVER, error); } if (status >= 400 && status < 500) { return createClassifiedError(`Client error (${status}): ${error.response?.statusText || 'Bad request'}`, ErrorCategory.CLIENT, error); } return null; } /** * Main error handler that processes different error types in sequence * @param error The error to handle * @returns A ClassifiedError appropriate for the error type * @public */ export function handleApiError(error) { // Create a logger for error handling const logger = createLogger('ErrorHandler'); logger.debug('Handling API error', { errorType: typeof error, isObject: error !== null && typeof error === 'object', errorKeys: error !== null && typeof error === 'object' ? Object.keys(error) : [], }); // If it's already a classified error, just return it if (error && typeof error === 'object' && 'category' in error) { logger.debug('Error is already classified', { category: error.category, message: error.message, }); return error; } // Try handling specific error types in order of specificity logger.debug('Checking for GraphQL-specific errors'); const graphqlError = handleGraphQLSpecificError(error); if (graphqlError) { logger.debug('Identified as GraphQL error', { category: graphqlError.category, message: graphqlError.message, }); return graphqlError; } logger.debug('Checking for network errors'); const networkError = handleNetworkError(error); if (networkError) { logger.debug('Identified as network error', { category: networkError.category, message: networkError.message, }); return networkError; } logger.debug('Checking for HTTP status errors'); const httpError = handleHttpStatusError(error); if (httpError) { logger.debug('Identified as HTTP status error', { category: httpError.category, message: httpError.message, }); return httpError; } // If no specific handler worked, create a generic classified error if (isError(error)) { logger.debug('No specific handler matched, creating generic error', { message: error.message, }); const category = classifyGraphQLError(error); return createClassifiedError(`DeepSource API error: ${error.message}`, category, error); } // Last resort for truly unknown errors logger.debug('Handling completely unknown error type', { error: String(error), }); return createClassifiedError('Unknown error occurred while communicating with DeepSource API', ErrorCategory.OTHER, error); } //# sourceMappingURL=handlers.js.map