refactoring handlers

This commit is contained in:
Boki 2025-06-21 10:33:03 -04:00
parent 59ab0940ae
commit ab0b7a5385
12 changed files with 1098 additions and 25 deletions

View file

@ -2,4 +2,5 @@ export * from './calculations/index';
export * from './common';
export * from './dateUtils';
export * from './generic-functions';
export * from './operation-context';
export * from './proxy';

View file

@ -0,0 +1,172 @@
/**
* OperationContext - Unified context for handler operations
*
* Provides streamlined access to:
* - Child loggers with hierarchical context
* - Database clients (MongoDB, PostgreSQL)
* - Contextual cache with automatic key prefixing
* - Shared resource management
*/
import { createCache, type CacheProvider } from '@stock-bot/cache';
import { getLogger, type Logger } from '@stock-bot/logger';
import { getDatabaseConfig } from '@stock-bot/config';
export class OperationContext {
public readonly logger: Logger;
public readonly mongodb: any; // MongoDB client - imported dynamically
public readonly postgres: any; // PostgreSQL client - imported dynamically
private static sharedCache: CacheProvider | null = null;
private static parentLoggers = new Map<string, Logger>();
private static databaseConfig: any = null;
constructor(
public readonly handlerName: string,
public readonly operationName: string,
parentLogger?: Logger
) {
// Create child logger from parent or create handler parent
const parent = parentLogger || this.getOrCreateParentLogger();
this.logger = parent.child(operationName, {
handler: handlerName,
operation: operationName
});
// Set up database access
this.mongodb = this.getDatabaseClient('mongodb');
this.postgres = this.getDatabaseClient('postgres');
}
private getDatabaseClient(type: 'mongodb' | 'postgres'): any {
try {
if (type === 'mongodb') {
// Dynamic import to avoid TypeScript issues during build
const { getMongoDBClient } = require('@stock-bot/mongodb-client');
return getMongoDBClient();
} else {
// Dynamic import to avoid TypeScript issues during build
const { getPostgreSQLClient } = require('@stock-bot/postgres-client');
return getPostgreSQLClient();
}
} catch (error) {
this.logger.warn(`${type} client not initialized, operations may fail`, { error });
return null;
}
}
private getOrCreateParentLogger(): Logger {
const parentKey = `${this.handlerName}-handler`;
if (!OperationContext.parentLoggers.has(parentKey)) {
const parentLogger = getLogger(parentKey);
OperationContext.parentLoggers.set(parentKey, parentLogger);
}
return OperationContext.parentLoggers.get(parentKey)!;
}
/**
* Get contextual cache with automatic key prefixing
* Keys are automatically prefixed as: "operations:handlerName:operationName:key"
*/
get cache(): CacheProvider {
if (!OperationContext.sharedCache) {
// Get Redis configuration from database config
if (!OperationContext.databaseConfig) {
OperationContext.databaseConfig = getDatabaseConfig();
}
const redisConfig = OperationContext.databaseConfig.dragonfly || {
host: 'localhost',
port: 6379,
db: 1
};
OperationContext.sharedCache = createCache({
keyPrefix: 'operations:',
shared: true, // Use singleton Redis connection
enableMetrics: true,
ttl: 3600, // Default 1 hour TTL
redisConfig
});
}
return this.createContextualCache();
}
private createContextualCache(): CacheProvider {
const contextPrefix = `${this.handlerName}:${this.operationName}:`;
// Return a proxy that automatically prefixes keys with context
return {
async get<T>(key: string): Promise<T | null> {
return OperationContext.sharedCache!.get(`${contextPrefix}${key}`);
},
async set<T>(key: string, value: T, options?: any): Promise<T | null> {
return OperationContext.sharedCache!.set(`${contextPrefix}${key}`, value, options);
},
async del(key: string): Promise<void> {
return OperationContext.sharedCache!.del(`${contextPrefix}${key}`);
},
async exists(key: string): Promise<boolean> {
return OperationContext.sharedCache!.exists(`${contextPrefix}${key}`);
},
async clear(): Promise<void> {
// Not implemented for contextual cache - use del() for specific keys
throw new Error('clear() not implemented for contextual cache - use del() for specific keys');
},
async keys(pattern: string): Promise<string[]> {
const fullPattern = `${contextPrefix}${pattern}`;
return OperationContext.sharedCache!.keys(fullPattern);
},
getStats() {
return OperationContext.sharedCache!.getStats();
},
async health(): Promise<boolean> {
return OperationContext.sharedCache!.health();
},
async waitForReady(timeout?: number): Promise<void> {
return OperationContext.sharedCache!.waitForReady(timeout);
},
isReady(): boolean {
return OperationContext.sharedCache!.isReady();
}
} as CacheProvider;
}
/**
* Factory method to create OperationContext
*/
static create(handlerName: string, operationName: string, parentLogger?: Logger): OperationContext {
return new OperationContext(handlerName, operationName, parentLogger);
}
/**
* Get cache key prefix for this operation context
*/
getCacheKeyPrefix(): string {
return `operations:${this.handlerName}:${this.operationName}:`;
}
/**
* Create a child context for sub-operations
*/
createChild(subOperationName: string): OperationContext {
return new OperationContext(
this.handlerName,
`${this.operationName}:${subOperationName}`,
this.logger
);
}
}
export default OperationContext;