refactoring handlers
This commit is contained in:
parent
59ab0940ae
commit
ab0b7a5385
12 changed files with 1098 additions and 25 deletions
|
|
@ -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';
|
||||
|
|
|
|||
172
libs/utils/src/operation-context.ts
Normal file
172
libs/utils/src/operation-context.ts
Normal 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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue