UNPKG

@ritas-inc/hanaqueryapi-client

Version:

TypeScript client for HANA Query API with full type safety and error handling

650 lines (529 loc) 22.2 kB
/** * Advanced Usage Examples * * This file demonstrates advanced patterns and use cases for the HANA Query API client, * including fluent API, request cancellation, parallel requests, and custom configurations. */ import { HanaQueryClient, createClient, type Plan, type ItemStatus, type WorkOrder, isNotFoundError, isSuccessResponse } from '../index.ts'; // ============================================================================ // Example 1: Fluent Request Builder API // ============================================================================ async function fluentRequestBuilder() { console.log('=== Fluent Request Builder API ==='); const client = new HanaQueryClient({ baseUrl: 'http://localhost:3001' }); try { // Using fluent API for complex requests console.log('Building complex requests with fluent API...'); // Simple fluent request const health = await client .request('/health') .timeout(5000) .execute(); if (isSuccessResponse(health)) { console.log(`✅ Health check via fluent API: ${health.data.status}`); } else { console.log(`❌ Health check via fluent API: ${health.problem.detail}`); } // Fluent request with query parameters const items = await client .request('/items/status') .query({ from: '2025-01-01', to: '2025-06-30' }) .timeout(60000) // Longer timeout for large dataset .retries(5) // More retries for important data .execute(); if (isSuccessResponse(items)) { console.log(`✅ Items via fluent API: ${items.metadata.count} items found`); } else { console.log(`❌ Items via fluent API: ${items.problem.detail}`); } // Multiple fluent requests with different configurations const planIds = [1, 2, 3]; const planRequests = planIds.map(planId => client .request(`/plans/${planId}`) .timeout(10000) .retries(2) .execute() .catch(error => ({ error, planId })) ); const planResults = await Promise.all(planRequests); console.log('\\nPlan requests results:'); planResults.forEach((result, index) => { if ('error' in result) { const errorMsg = result.error instanceof Error ? result.error.message : String(result.error); console.log(` Plan ${result.planId}: ❌ ${errorMsg}`); } else if (isSuccessResponse(result)) { console.log(` Plan ${planIds[index]}: ✅ ${result.data.plan_status}`); } else { console.log(` Plan ${planIds[index]}: ❌ Error response received`); } }); } catch (error: unknown) { if (error instanceof Error) { console.error('Error in fluent API example:', error instanceof Error ? error.message : String(error)); } else { console.error('Error in fluent API example:', error); } } } // ============================================================================ // Example 2: Request Cancellation and Timeouts // ============================================================================ async function requestCancellationAndTimeouts() { console.log('\\n=== Request Cancellation and Timeouts ==='); const client = new HanaQueryClient({ baseUrl: 'http://localhost:3001' }); // Example 1: Manual request cancellation console.log('Testing manual request cancellation...'); const controller = new AbortController(); // Start a request that we'll cancel const promise = client.getItemHierarchies({ signal: controller.signal, timeout: 30000 }); // Cancel the request after 2 seconds setTimeout(() => { console.log('⏹️ Cancelling request after 2 seconds...'); controller.abort(); }, 2000); try { await promise; console.log('✅ Request completed before cancellation'); } catch (error: unknown) { if (error instanceof Error) { if (error.name === 'AbortError' || error.message.includes('abort')) { console.log('✅ Request successfully cancelled'); } else { console.log('❌ Request failed for other reason:', error.message); } } else { console.log('❌ Request failed for unknown reason:', error); } } // Example 2: Racing requests with timeout console.log('\\nTesting request racing with timeout...'); async function raceWithTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> { const timeoutPromise = new Promise<never>((_, reject) => { setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs); }); return Promise.race([promise, timeoutPromise]); } try { const quickResult = await raceWithTimeout( client.getHealth(), 5000 // 5 second timeout ); console.log('✅ Quick request completed:', quickResult.data.status); } catch (error: unknown) { if (error instanceof Error) { console.log('❌ Request timed out:', error instanceof Error ? error.message : String(error)); } else { console.log('❌ Request timed out:', error); } } // Example 3: Timeout configuration per endpoint type console.log('\\nTesting endpoint-specific timeouts...'); const timeoutConfigs = { health: 5000, // Fast endpoint plans: 15000, // Medium endpoint items: 60000, // Slow endpoint with large data hierarchies: 90000 // Slowest endpoint }; try { const [health, plans] = await Promise.all([ client.getHealth({ timeout: timeoutConfigs.health }), client.getPlans({ timeout: timeoutConfigs.plans }) ]); console.log(`✅ Health (${timeoutConfigs.health}ms): ${health.data.status}`); console.log(`✅ Plans (${timeoutConfigs.plans}ms): ${plans.metadata.count} plans`); } catch (error: unknown) { if (error instanceof Error) { console.log('❌ Timeout configuration test failed:', error instanceof Error ? error.message : String(error)); } else { console.log('❌ Timeout configuration test failed:', error); } } } // ============================================================================ // Example 3: Parallel and Batch Operations // ============================================================================ async function parallelAndBatchOperations() { console.log('\\n=== Parallel and Batch Operations ==='); const client = new HanaQueryClient({ baseUrl: 'http://localhost:3001' }); // Example 1: Parallel independent requests console.log('Running parallel independent requests...'); const startTime = Date.now(); try { const [health, docs, plans, hierarchies] = await Promise.allSettled([ client.getHealth(), client.getDocs(), client.getPlans(), client.getItemHierarchies() ]); const duration = Date.now() - startTime; console.log(`✅ Parallel requests completed in ${duration}ms`); // Process results if (health.status === 'fulfilled') { console.log(` Health: ✅ ${health.value.data.status}`); } else { console.log(` Health: ❌ ${health.reason.message}`); } if (docs.status === 'fulfilled') { console.log(` Docs: ✅ ${docs.value.data.name} v${docs.value.data.version}`); } else { console.log(` Docs: ❌ ${docs.reason.message}`); } if (plans.status === 'fulfilled') { console.log(` Plans: ✅ ${plans.value.metadata.count} plans`); } else { console.log(` Plans: ❌ ${plans.reason.message}`); } if (hierarchies.status === 'fulfilled') { console.log(` Hierarchies: ✅ ${hierarchies.value.metadata.count} relationships`); } else { console.log(` Hierarchies: ❌ ${hierarchies.reason.message}`); } } catch (error: unknown) { console.error('Parallel requests failed:', error instanceof Error ? error.message : String(error)); } // Example 2: Batch processing with controlled concurrency console.log('\\nTesting controlled concurrency batch processing...'); async function processBatch<T, R>( items: T[], processor: (item: T) => Promise<R>, batchSize: number = 3 ): Promise<R[]> { const results: R[] = []; for (let i = 0; i < items.length; i += batchSize) { const batch = items.slice(i, i + batchSize); console.log(` Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(items.length / batchSize)}`); const batchResults = await Promise.allSettled( batch.map(processor) ); for (const result of batchResults) { if (result.status === 'fulfilled') { results.push(result.value); } else { console.log(` ❌ Batch item failed: ${result.reason.message}`); } } } return results; } try { // Get plan IDs first const plansResponse = await client.getPlans(); const planIds = plansResponse.data.plans.map(p => p.plan_id).slice(0, 6); // Limit for demo // Process plans in batches const planDetails = await processBatch( planIds, async (planId) => { const plan = await client.getPlan(planId); return { planId, plan: plan.data.plan }; }, 2 // Process 2 plans at a time ); console.log(`✅ Processed ${planDetails.length} plans in batches`); } catch (error: unknown) { console.error('Batch processing failed:', error instanceof Error ? error.message : String(error)); } // Example 3: Data aggregation from multiple endpoints console.log('\\nAggregating data from multiple endpoints...'); try { const aggregatedData = await client.getPlans().then(async (plansResponse) => { const plans = plansResponse.data.plans.slice(0, 3); // Limit for demo // Get detailed data for each plan const planDataPromises = plans.map(async (plan) => { const [productsResult, workOrdersResult] = await Promise.allSettled([ client.getPlanProducts(plan.plan_id), client.getPlanWorkOrders(plan.plan_id) ]); return { plan, products: productsResult.status === 'fulfilled' ? productsResult.value.data.products : [], workOrders: workOrdersResult.status === 'fulfilled' ? workOrdersResult.value.data.workOrders : [], hasProducts: productsResult.status === 'fulfilled', hasWorkOrders: workOrdersResult.status === 'fulfilled' }; }); return Promise.all(planDataPromises); }); console.log(`✅ Aggregated data for ${aggregatedData.length} plans:`); aggregatedData.forEach(data => { console.log(` Plan ${data.plan.plan_id}: ${data.products.length} products, ${data.workOrders.length} work orders`); }); } catch (error: unknown) { console.error('Data aggregation failed:', error instanceof Error ? error.message : String(error)); } } // ============================================================================ // Example 4: Custom Client Configurations // ============================================================================ async function customClientConfigurations() { console.log('\\n=== Custom Client Configurations ==='); // Example 1: High-performance client for bulk operations const bulkClient = createClient({ baseUrl: 'http://localhost:3001' }, { timeout: 120000, // 2 minutes for large datasets retries: 5, // More retries for important operations retryDelay: 2000, // Longer delay between retries enableLogging: true, logLevel: 'info', headers: { 'X-Client-Type': 'bulk-operations', 'X-Request-Priority': 'high' } }); console.log('Created high-performance bulk client'); // Example 2: Quick client for real-time operations const quickClient = createClient({ baseUrl: 'http://localhost:3001' }, { timeout: 5000, // Quick timeout retries: 1, // Minimal retries retryDelay: 500, // Fast retry enableLogging: false, // No logging for performance headers: { 'X-Client-Type': 'real-time', 'X-Request-Priority': 'urgent' } }); console.log('Created quick real-time client'); // Example 3: Debug client with extensive logging const debugClient = createClient({ baseUrl: 'http://localhost:3001' }, { enableLogging: true, logLevel: 'debug', timeout: 30000, retries: 0, // No retries to see all failures headers: { 'X-Client-Type': 'debug', 'X-Debug-Mode': 'enabled' } }); console.log('Created debug client with extensive logging'); // Test the different clients try { console.log('\\nTesting different client configurations...'); // Quick health checks with different clients const [bulkHealth, quickHealth, debugHealth] = await Promise.allSettled([ bulkClient.getHealth(), quickClient.getHealth(), debugClient.getHealth() ]); console.log('Client test results:'); console.log(` Bulk client: ${bulkHealth.status === 'fulfilled' ? '✅' : '❌'}`); console.log(` Quick client: ${quickHealth.status === 'fulfilled' ? '✅' : '❌'}`); console.log(` Debug client: ${debugHealth.status === 'fulfilled' ? '✅' : '❌'}`); // Demonstrate configuration updates console.log('\\nUpdating client configuration dynamically...'); quickClient.updateConfig({ timeout: 10000, // Increase timeout enableLogging: true, logLevel: 'warn' }); console.log('✅ Quick client configuration updated'); // Show current configuration const config = quickClient.getConfig(); console.log(`Updated timeout: ${config.timeout}ms`); console.log(`Logging enabled: ${config.enableLogging}`); } catch (error: unknown) { console.error('Client configuration test failed:', error instanceof Error ? error.message : String(error)); } } // ============================================================================ // Example 5: Advanced Data Processing Patterns // ============================================================================ async function advancedDataProcessing() { console.log('\\n=== Advanced Data Processing Patterns ==='); const client = new HanaQueryClient({ baseUrl: 'http://localhost:3001' }); // Example 1: Streaming-like data processing console.log('Implementing streaming-like data processing...'); async function* processItemsInChunks(chunkSize: number = 1000) { try { const items = await client.getItems(); const allItems = items.data.items; for (let i = 0; i < allItems.length; i += chunkSize) { const chunk = allItems.slice(i, i + chunkSize); yield { chunk, chunkIndex: Math.floor(i / chunkSize), totalChunks: Math.ceil(allItems.length / chunkSize), itemsInChunk: chunk.length, totalItems: allItems.length }; } } catch (error: unknown) { throw error; } } try { console.log('Processing items in chunks...'); let processedCount = 0; for await (const { chunk, chunkIndex, totalChunks, itemsInChunk } of processItemsInChunks(500)) { // Process each chunk const lowStockInChunk = chunk.filter((item: any) => item.onhand < item.min); processedCount += itemsInChunk; console.log(` Chunk ${chunkIndex + 1}/${totalChunks}: ${itemsInChunk} items, ${lowStockInChunk.length} low stock`); // Simulate processing delay await new Promise(resolve => setTimeout(resolve, 100)); } console.log(`✅ Processed ${processedCount} items in chunks`); } catch (error: unknown) { console.error('Chunk processing failed:', error instanceof Error ? error.message : String(error)); } // Example 2: Complex data transformation pipeline console.log('\\nImplementing data transformation pipeline...'); interface PlanAnalysis { plan: Plan; efficiency: number; status: 'excellent' | 'good' | 'needs_attention' | 'critical'; products: number; workOrders: number; completionRate: number; } async function analyzePlanEfficiency(): Promise<PlanAnalysis[]> { const plans = await client.getPlans(); const analyses = await Promise.allSettled( plans.data.plans.map(async (plan): Promise<PlanAnalysis> => { const [productsResult, workOrdersResult] = await Promise.allSettled([ client.getPlanProducts(plan.plan_id), client.getPlanWorkOrders(plan.plan_id) ]); const products = productsResult.status === 'fulfilled' ? productsResult.value.data.products.length : 0; const workOrders = workOrdersResult.status === 'fulfilled' ? workOrdersResult.value.data.workOrders : []; // Calculate efficiency metrics const totalPlanned = workOrders.reduce((sum, wo) => sum + wo.order_plannedqty, 0); const totalCompleted = workOrders.reduce((sum, wo) => sum + wo.order_completedqty, 0); const completionRate = totalPlanned > 0 ? (totalCompleted / totalPlanned) : 0; const efficiency = plan.products_total > 0 ? (plan.products_closed / plan.products_total) * 100 : 0; let status: PlanAnalysis['status']; if (efficiency >= 90) status = 'excellent'; else if (efficiency >= 70) status = 'good'; else if (efficiency >= 50) status = 'needs_attention'; else status = 'critical'; return { plan, efficiency, status, products, workOrders: workOrders.length, completionRate: completionRate * 100 }; }) ); return analyses .filter((result): result is PromiseFulfilledResult<PlanAnalysis> => result.status === 'fulfilled') .map(result => result.value); } try { const analyses = await analyzePlanEfficiency(); console.log(`✅ Analyzed ${analyses.length} plans:`); // Group by status const statusGroups = analyses.reduce((groups, analysis) => { if (!groups[analysis.status]) groups[analysis.status] = []; groups[analysis.status].push(analysis); return groups; }, {} as Record<string, PlanAnalysis[]>); Object.entries(statusGroups).forEach(([status, group]) => { console.log(` ${status.toUpperCase()}: ${group.length} plans`); group.forEach(analysis => { console.log(` Plan ${analysis.plan.plan_id}: ${analysis.efficiency.toFixed(1)}% efficiency`); }); }); } catch (error: unknown) { console.error('Plan analysis failed:', error instanceof Error ? error.message : String(error)); } // Example 3: Real-time data monitoring simulation console.log('\\nSimulating real-time data monitoring...'); async function monitorApiHealth(intervalMs: number = 5000, duration: number = 20000) { console.log(`Starting health monitoring (${duration / 1000}s duration, ${intervalMs / 1000}s interval)...`); const startTime = Date.now(); let checkCount = 0; const healthHistory: Array<{ timestamp: number; status: string; uptime: number; responseTime: number }> = []; const monitoringInterval = setInterval(async () => { try { const requestStart = Date.now(); const health = await client.getHealth({ timeout: 3000 }); const responseTime = Date.now() - requestStart; checkCount++; healthHistory.push({ timestamp: Date.now(), status: health.data.status, uptime: health.data.uptime, responseTime }); console.log(` Check ${checkCount}: ${health.data.status} (${responseTime}ms)`); } catch (error: unknown) { console.log(` Check ${++checkCount}: ❌ ${error instanceof Error ? error.message : String(error)}`); healthHistory.push({ timestamp: Date.now(), status: 'error', uptime: 0, responseTime: 0 }); } // Stop after duration if (Date.now() - startTime >= duration) { clearInterval(monitoringInterval); // Summary const avgResponseTime = healthHistory .filter(h => h.status === 'ok') .reduce((sum, h, _, arr) => sum + h.responseTime / arr.length, 0); const successRate = (healthHistory.filter(h => h.status === 'ok').length / healthHistory.length) * 100; console.log(`✅ Monitoring complete:`); console.log(` Total checks: ${checkCount}`); console.log(` Success rate: ${successRate.toFixed(1)}%`); console.log(` Average response time: ${avgResponseTime.toFixed(1)}ms`); } }, intervalMs); } try { await monitorApiHealth(3000, 15000); // 15 seconds, check every 3 seconds } catch (error: unknown) { console.error('Health monitoring failed:', error instanceof Error ? error.message : String(error)); } } // ============================================================================ // Main Execution // ============================================================================ async function runAdvancedUsageExamples() { console.log('🚀 HANA Query API Client - Advanced Usage Examples'); console.log('='.repeat(60)); try { await fluentRequestBuilder(); await requestCancellationAndTimeouts(); await parallelAndBatchOperations(); await customClientConfigurations(); await advancedDataProcessing(); console.log('\\n✅ All advanced usage examples completed!'); } catch (error: unknown) { console.error('\\n❌ Advanced usage examples failed:', error); } } // Run examples if this file is executed directly if (import.meta.url === `file://${process.argv[1]}`) { runAdvancedUsageExamples().catch(console.error); } export { fluentRequestBuilder, requestCancellationAndTimeouts, parallelAndBatchOperations, customClientConfigurations, advancedDataProcessing, runAdvancedUsageExamples };