@ritas-inc/hanaqueryapi-client
Version:
TypeScript client for HANA Query API with full type safety and error handling
542 lines (449 loc) ⢠17.5 kB
text/typescript
/**
* Error Handling Examples
*
* This file demonstrates comprehensive error handling strategies
* for the HANA Query API client.
*/
import {
HanaQueryClient,
HanaQueryClientError,
NetworkError,
TimeoutError,
ValidationError,
AuthorizationError,
NotFoundError,
ServerError,
UnknownError,
isRetryableError,
getRetryDelay,
isNetworkError,
isTimeoutError,
isValidationError,
isNotFoundError,
isServerError
} from '../index.ts';
// ============================================================================
// Example 1: Basic Error Handling
// ============================================================================
async function basicErrorHandling() {
console.log('=== Basic Error Handling ===');
const client = new HanaQueryClient({ baseUrl: 'http://localhost:3001' });
try {
// This will succeed if API is available
const health = await client.getHealth();
console.log('ā
Health check successful:', health.data.status);
} catch (error: unknown) {
// Handle any errors
if (error instanceof HanaQueryClientError) {
console.error('ā API Error:', {
type: error.type,
message: error instanceof Error ? error.message : String(error),
statusCode: error.statusCode
});
} else {
console.error('ā Unexpected Error:', error);
}
}
}
// ============================================================================
// Example 2: Specific Error Type Handling
// ============================================================================
async function specificErrorHandling() {
console.log('\\n=== Specific Error Type Handling ===');
const client = new HanaQueryClient({
baseUrl: 'http://invalid-url-that-does-not-exist.com', // This will cause network errors
timeout: 1000 // Very short timeout to trigger timeout errors
});
try {
console.log('Attempting request with invalid URL...');
await client.getHealth();
} catch (error: unknown) {
if (error instanceof NetworkError) {
console.log('š Network Error detected:');
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
console.log(` Original error: ${error.originalError?.message}`);
} else if (error instanceof TimeoutError) {
console.log('ā±ļø Timeout Error detected:');
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
console.log(` Duration: ${error.context?.duration}ms`);
} else if (error instanceof ValidationError) {
console.log('āļø Validation Error detected:');
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
} else if (error instanceof AuthorizationError) {
console.log('š Authorization Error detected:');
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
console.log(` Status Code: ${error.statusCode}`);
} else if (error instanceof NotFoundError) {
console.log('š Not Found Error detected:');
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
console.log(` Resource: ${error.context?.url}`);
} else if (error instanceof ServerError) {
console.log('š„ Server Error detected:');
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
console.log(` Status Code: ${error.statusCode}`);
console.log(` Problem Details:`, error.problemDetails);
} else {
console.log('ā Unknown Error:');
console.log(` Type: ${error && typeof error === 'object' && 'constructor' in error ? error.constructor.name : 'Unknown'}`);
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
// ============================================================================
// Example 3: Error Type Guards
// ============================================================================
async function errorTypeGuards() {
console.log('\\n=== Error Type Guards ===');
const client = new HanaQueryClient({
baseUrl: 'http://localhost:99999', // Non-existent port
timeout: 2000
});
try {
await client.getPlans();
} catch (error: unknown) {
console.log('Error caught, analyzing with type guards:');
if (isNetworkError(error)) {
console.log('š Type guard confirmed: Network Error');
console.log(` Can retry: ${isRetryableError(error)}`);
} else if (isTimeoutError(error)) {
console.log('ā±ļø Type guard confirmed: Timeout Error');
console.log(` Can retry: ${isRetryableError(error)}`);
} else if (isValidationError(error)) {
console.log('āļø Type guard confirmed: Validation Error');
console.log(` Can retry: ${isRetryableError(error)}`);
} else if (isNotFoundError(error)) {
console.log('š Type guard confirmed: Not Found Error');
console.log(` Can retry: ${isRetryableError(error)}`);
} else if (isServerError(error)) {
console.log('š„ Type guard confirmed: Server Error');
console.log(` Can retry: ${isRetryableError(error)}`);
} else {
console.log('ā Type guard: Unknown error type');
}
}
}
// ============================================================================
// Example 4: Graceful Error Handling for Expected Scenarios
// ============================================================================
async function gracefulErrorHandling() {
console.log('\\n=== Graceful Error Handling ===');
const client = new HanaQueryClient({ baseUrl: 'http://localhost:3001' });
// Function to safely get plan work orders (which may not exist)
async function safeGetPlanWorkOrders(planId: number | string) {
try {
const workOrders = await client.getPlanWorkOrders(planId);
return {
success: true,
data: workOrders.data.workOrders,
count: workOrders.metadata.count
};
} catch (error: unknown) {
if (isNotFoundError(error)) {
// This is expected for plans without work orders
return {
success: true,
data: [],
count: 0,
note: 'No work orders found for this plan'
};
} else {
// Unexpected error
return {
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
}
try {
// Get plans first
const plans = await client.getPlans();
const firstPlanId = plans.data.plans[0]?.plan_id;
if (firstPlanId) {
console.log(`Checking work orders for plan ${firstPlanId}...`);
const result = await safeGetPlanWorkOrders(firstPlanId);
if (result.success) {
console.log(`ā
Found ${result.count} work orders`);
if (result.note) {
console.log(` Note: ${result.note}`);
}
} else {
console.log(`ā Failed to get work orders: ${result.error}`);
}
}
} catch (error: unknown) {
console.error('Error in graceful handling example:', error instanceof Error ? error.message : String(error));
}
}
// ============================================================================
// Example 5: Custom Retry Logic
// ============================================================================
async function customRetryLogic() {
console.log('\\n=== Custom Retry Logic ===');
const client = new HanaQueryClient({
baseUrl: 'http://localhost:3001',
retries: 0 // Disable built-in retries for this example
});
async function retryableOperation<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
let lastError: HanaQueryClientError | null = null;
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
try {
console.log(` Attempt ${attempt}/${maxRetries + 1}`);
const result = await operation();
if (attempt > 1) {
console.log(` ā
Success on attempt ${attempt}`);
}
return result;
} catch (error: unknown) {
if (error instanceof HanaQueryClientError) {
lastError = error;
console.log(` ā Attempt ${attempt} failed: ${error.type} - ${error instanceof Error ? error.message : String(error)}`);
// Check if error is retryable
if (!isRetryableError(error)) {
console.log(` š« Error is not retryable, stopping`);
break;
}
// Don't wait after the last attempt
if (attempt <= maxRetries) {
const delay = getRetryDelay(attempt, baseDelay, 10000);
console.log(` ā³ Waiting ${delay}ms before retry...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
} else {
throw error; // Re-throw unexpected errors
}
}
}
throw lastError!;
}
try {
console.log('Testing custom retry logic with network error...');
// This will fail but demonstrate retry logic
const invalidClient = new HanaQueryClient({
baseUrl: 'http://localhost:99999',
retries: 0
});
await retryableOperation(
() => invalidClient.getHealth(),
3, // 3 retries
500 // 500ms base delay
);
} catch (error: unknown) {
console.log(`Final error after all retries: ${error instanceof Error ? error.message : String(error)}`);
}
}
// ============================================================================
// Example 6: Error Context and Debugging
// ============================================================================
async function errorContextAndDebugging() {
console.log('\\n=== Error Context and Debugging ===');
const client = new HanaQueryClient({
baseUrl: 'http://localhost:3001',
enableLogging: true,
logLevel: 'debug'
});
// Function to log detailed error information
function logErrorDetails(error: HanaQueryClientError) {
console.log('š Detailed Error Analysis:');
console.log(` Type: ${error.type}`);
console.log(` Message: ${error instanceof Error ? error.message : String(error)}`);
console.log(` Status Code: ${error.statusCode || 'N/A'}`);
if (error.context) {
console.log(' Request Context:');
console.log(` Method: ${error.context.method}`);
console.log(` URL: ${error.context.url}`);
console.log(` Duration: ${error.context.duration || 'N/A'}ms`);
console.log(` Attempt: ${error.context.attempt || 'N/A'}/${error.context.maxAttempts || 'N/A'}`);
}
if (error.problemDetails) {
console.log(' Problem Details:');
console.log(` Title: ${error.problemDetails.title}`);
console.log(` Detail: ${error.problemDetails.detail}`);
console.log(` Instance: ${error.problemDetails.instance}`);
if (error.problemDetails.issues.length > 0) {
console.log(` Issues: ${error.problemDetails.issues.join(', ')}`);
}
}
if (error.originalError) {
console.log(` Original Error: ${error.originalError.message}`);
}
// Error serialization for logging
console.log(' JSON Representation:', JSON.stringify(error.toJSON(), null, 2));
}
try {
// Test with invalid parameters to trigger validation error
await client.getSales({
from: 'invalid-date',
to: 'also-invalid'
} as any);
} catch (error: unknown) {
if (error instanceof HanaQueryClientError) {
logErrorDetails(error);
}
}
}
// ============================================================================
// Example 7: Production-Ready Error Handling Strategy
// ============================================================================
class RobustAPIService {
private client: HanaQueryClient;
private config: {
baseUrl: string;
enableLogging?: boolean;
logLevel?: 'debug' | 'info' | 'warn' | 'error';
timeout?: number;
retries?: number;
};
constructor(config: {
baseUrl: string;
enableLogging?: boolean;
logLevel?: 'debug' | 'info' | 'warn' | 'error';
timeout?: number;
retries?: number;
}) {
this.config = config;
this.client = new HanaQueryClient({
baseUrl: config.baseUrl,
enableLogging: config.enableLogging ?? false,
logLevel: config.logLevel ?? 'error',
timeout: config.timeout ?? 30000,
retries: config.retries ?? 3
});
}
/**
* Wrapper method that handles errors consistently
*/
private async safeApiCall<T>(
operation: () => Promise<T>,
fallbackValue?: T
): Promise<{ success: true; data: T } | { success: false; error: string; type: string }> {
try {
const data = await operation();
return { success: true, data };
} catch (error: unknown) {
if (error instanceof HanaQueryClientError) {
// Log error details if logging is enabled
if (this.config.enableLogging) {
console.error('API Error Details:', error.toJSON());
}
return {
success: false,
error: this.getUserFriendlyMessage(error),
type: error.type
};
} else {
// Unexpected error
console.error('Unexpected API error:', error);
return {
success: false,
error: 'An unexpected error occurred. Please try again.',
type: 'unknown_error'
};
}
}
}
/**
* Convert technical errors to user-friendly messages
*/
private getUserFriendlyMessage(error: HanaQueryClientError): string {
switch (error.type) {
case 'network_error':
return 'Unable to connect to the server. Please check your internet connection and try again.';
case 'timeout_error':
return 'The request is taking longer than expected. Please try again.';
case 'validation_error':
return 'Invalid input provided. Please check your data and try again.';
case 'authorization_error':
return 'You are not authorized to access this resource.';
case 'not_found_error':
return 'The requested resource was not found.';
case 'server_error':
return 'A server error occurred. Please try again later.';
default:
return 'An error occurred while processing your request.';
}
}
/**
* Public methods with consistent error handling
*/
async getHealthStatus() {
return this.safeApiCall(() => this.client.getHealth());
}
async getProductionPlans() {
return this.safeApiCall(() => this.client.getPlans(), { data: { plans: [] }, metadata: { count: 0 } });
}
async getPlanDetails(planId: number) {
return this.safeApiCall(() => this.client.getPlan(planId));
}
async getInventoryStatus() {
return this.safeApiCall(() => this.client.getItems());
}
}
async function productionReadyErrorHandling() {
console.log('\\n=== Production-Ready Error Handling ===');
const apiService = new RobustAPIService({
baseUrl: 'http://localhost:3001',
enableLogging: true,
logLevel: 'info'
});
// Test the service methods
console.log('Testing robust API service...');
const healthResult = await apiService.getHealthStatus();
if (healthResult.success) {
console.log('ā
Health check successful:', healthResult.data.data.status);
} else {
console.log('ā Health check failed:', healthResult.error);
}
const plansResult = await apiService.getProductionPlans();
if (plansResult.success) {
console.log(`ā
Found ${plansResult.data.metadata.count} production plans`);
} else {
console.log('ā Failed to get plans:', plansResult.error);
console.log('Error type:', plansResult.type);
}
// Test with invalid plan ID
const invalidPlanResult = await apiService.getPlanDetails(99999);
if (invalidPlanResult.success) {
console.log('ā
Plan details retrieved');
} else {
console.log('ā Plan not found:', invalidPlanResult.error);
}
}
// ============================================================================
// Main Execution
// ============================================================================
async function runErrorHandlingExamples() {
console.log('š”ļø HANA Query API Client - Error Handling Examples');
console.log('='.repeat(60));
try {
await basicErrorHandling();
await specificErrorHandling();
await errorTypeGuards();
await gracefulErrorHandling();
await customRetryLogic();
await errorContextAndDebugging();
await productionReadyErrorHandling();
console.log('\\nā
All error handling examples completed!');
} catch (error: unknown) {
console.error('\\nā Error handling examples failed:', error);
}
}
// Run examples if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
runErrorHandlingExamples().catch(console.error);
}
export {
basicErrorHandling,
specificErrorHandling,
errorTypeGuards,
gracefulErrorHandling,
customRetryLogic,
errorContextAndDebugging,
productionReadyErrorHandling,
RobustAPIService,
runErrorHandlingExamples
};