@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
text/typescript
/**
* 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
};