huge refactor to remove depenencie hell and add typesafe container
This commit is contained in:
parent
28b9822d55
commit
843a7b9b9b
148 changed files with 3603 additions and 2378 deletions
|
|
@ -1,14 +1,17 @@
|
|||
import type { Collection } from 'mongodb';
|
||||
import { createNamespacedCache } from '@stock-bot/cache';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type {
|
||||
HandlerConfigWithSchedule,
|
||||
IServiceContainer,
|
||||
import type {
|
||||
ExecutionContext,
|
||||
IHandler
|
||||
HandlerConfigWithSchedule,
|
||||
HandlerMetadata,
|
||||
IHandler,
|
||||
IServiceContainer,
|
||||
JobHandler,
|
||||
ServiceTypes,
|
||||
} from '@stock-bot/types';
|
||||
import { fetch } from '@stock-bot/utils';
|
||||
import { createNamespacedCache } from '@stock-bot/cache';
|
||||
import { handlerRegistry } from '../registry/handler-registry';
|
||||
// Handler registry is now injected, not imported
|
||||
import { createJobHandler } from '../utils/create-job-handler';
|
||||
|
||||
/**
|
||||
|
|
@ -38,16 +41,16 @@ export interface JobScheduleOptions {
|
|||
* Provides common functionality and structure for queue/event operations
|
||||
*/
|
||||
export abstract class BaseHandler implements IHandler {
|
||||
// Direct service properties - flattened for cleaner access
|
||||
readonly logger;
|
||||
readonly cache;
|
||||
readonly globalCache;
|
||||
readonly queue;
|
||||
readonly proxy;
|
||||
readonly browser;
|
||||
readonly mongodb;
|
||||
readonly postgres;
|
||||
readonly questdb;
|
||||
// Direct service properties - flattened for cleaner access with proper types
|
||||
readonly logger: ServiceTypes['logger'];
|
||||
readonly cache: ServiceTypes['cache'];
|
||||
readonly globalCache: ServiceTypes['globalCache'];
|
||||
readonly queue: ServiceTypes['queue'];
|
||||
readonly proxy: ServiceTypes['proxy'];
|
||||
readonly browser: ServiceTypes['browser'];
|
||||
readonly mongodb: ServiceTypes['mongodb'];
|
||||
readonly postgres: ServiceTypes['postgres'];
|
||||
readonly questdb: ServiceTypes['questdb'];
|
||||
|
||||
private handlerName: string;
|
||||
|
||||
|
|
@ -109,8 +112,8 @@ export abstract class BaseHandler implements IHandler {
|
|||
}
|
||||
|
||||
async scheduleOperation(
|
||||
operation: string,
|
||||
payload: unknown,
|
||||
operation: string,
|
||||
payload: unknown,
|
||||
options?: JobScheduleOptions
|
||||
): Promise<void> {
|
||||
if (!this.queue) {
|
||||
|
|
@ -122,7 +125,7 @@ export abstract class BaseHandler implements IHandler {
|
|||
operation,
|
||||
payload,
|
||||
};
|
||||
|
||||
|
||||
await queue.add(operation, jobData, options || {});
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +165,7 @@ export abstract class BaseHandler implements IHandler {
|
|||
* Example: handler 'webshare' creates namespace 'webshare:api' -> keys will be 'cache:data-ingestion:webshare:api:*'
|
||||
*/
|
||||
protected createNamespacedCache(subNamespace: string) {
|
||||
return createNamespacedCache(this.cache, `${this.handlerName}:${subNamespace}`);
|
||||
return createNamespacedCache(this.cache || null, `${this.handlerName}:${subNamespace}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,36 +200,36 @@ export abstract class BaseHandler implements IHandler {
|
|||
// Don't add 'cache:' prefix since the cache already has its own prefix
|
||||
return this.cache.del(`${this.handlerName}:${key}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global cache with key
|
||||
*/
|
||||
protected async globalCacheSet(key: string, value: any, ttl?: number): Promise<void> {
|
||||
if (!this.globalCache) {
|
||||
return;
|
||||
}
|
||||
return this.globalCache.set(key, value, ttl);
|
||||
|
||||
/**
|
||||
* Set global cache with key
|
||||
*/
|
||||
protected async globalCacheSet(key: string, value: any, ttl?: number): Promise<void> {
|
||||
if (!this.globalCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global cache with key
|
||||
*/
|
||||
protected async globalCacheGet<T = any>(key: string): Promise<T | null> {
|
||||
if (!this.globalCache) {
|
||||
return null;
|
||||
}
|
||||
return this.globalCache.get(key);
|
||||
return this.globalCache.set(key, value, ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global cache with key
|
||||
*/
|
||||
protected async globalCacheGet<T = any>(key: string): Promise<T | null> {
|
||||
if (!this.globalCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete global cache with key
|
||||
*/
|
||||
protected async globalCacheDel(key: string): Promise<void> {
|
||||
if (!this.globalCache) {
|
||||
return;
|
||||
}
|
||||
return this.globalCache.del(key);
|
||||
return this.globalCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete global cache with key
|
||||
*/
|
||||
protected async globalCacheDel(key: string): Promise<void> {
|
||||
if (!this.globalCache) {
|
||||
return;
|
||||
}
|
||||
return this.globalCache.del(key);
|
||||
}
|
||||
/**
|
||||
* Schedule operation with delay in seconds
|
||||
*/
|
||||
|
|
@ -238,7 +241,7 @@ export abstract class BaseHandler implements IHandler {
|
|||
): Promise<void> {
|
||||
return this.scheduleOperation(operation, payload, {
|
||||
delay: delaySeconds * 1000,
|
||||
...additionalOptions
|
||||
...additionalOptions,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -294,27 +297,45 @@ export abstract class BaseHandler implements IHandler {
|
|||
// }
|
||||
|
||||
/**
|
||||
* Register this handler using decorator metadata
|
||||
* Automatically reads @Handler, @Operation, and @QueueSchedule decorators
|
||||
* Create handler configuration with job handlers
|
||||
* This is used by the scanner to create the actual handler configuration
|
||||
*/
|
||||
register(serviceName?: string): void {
|
||||
const constructor = this.constructor as any;
|
||||
const handlerName = constructor.__handlerName || this.handlerName;
|
||||
const operations = constructor.__operations || [];
|
||||
const schedules = constructor.__schedules || [];
|
||||
createHandlerConfig(): HandlerConfigWithSchedule {
|
||||
const metadata = (this.constructor as typeof BaseHandler).extractMetadata();
|
||||
if (!metadata) {
|
||||
throw new Error('Handler metadata not found');
|
||||
}
|
||||
|
||||
// Create operation handlers from decorator metadata
|
||||
const operationHandlers: Record<string, any> = {};
|
||||
for (const op of operations) {
|
||||
operationHandlers[op.name] = createJobHandler(async payload => {
|
||||
const operationHandlers: Record<string, JobHandler> = {};
|
||||
for (const opName of metadata.operations) {
|
||||
operationHandlers[opName] = createJobHandler(async (payload: any) => {
|
||||
const context: ExecutionContext = {
|
||||
type: 'queue',
|
||||
metadata: { source: 'queue', timestamp: Date.now() },
|
||||
};
|
||||
return await this.execute(op.name, payload, context);
|
||||
return await this.execute(opName, payload, context);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: metadata.name,
|
||||
operations: operationHandlers,
|
||||
scheduledJobs: metadata.scheduledJobs,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract handler metadata from decorators
|
||||
* This returns metadata only - actual handler instances are created by the scanner
|
||||
*/
|
||||
static extractMetadata(): HandlerMetadata | null {
|
||||
const constructor = this as any;
|
||||
const handlerName = constructor.__handlerName;
|
||||
if (!handlerName) return null;
|
||||
|
||||
const operations = constructor.__operations || [];
|
||||
const schedules = constructor.__schedules || [];
|
||||
|
||||
// Create scheduled jobs from decorator metadata
|
||||
const scheduledJobs = schedules.map((schedule: any) => {
|
||||
// Find the operation name from the method name
|
||||
|
|
@ -326,27 +347,15 @@ export abstract class BaseHandler implements IHandler {
|
|||
priority: schedule.priority || 5,
|
||||
immediately: schedule.immediately || false,
|
||||
description: schedule.description || `${handlerName} ${schedule.operation}`,
|
||||
payload: this.getScheduledJobPayload?.(schedule.operation),
|
||||
};
|
||||
});
|
||||
|
||||
const config: HandlerConfigWithSchedule = {
|
||||
return {
|
||||
name: handlerName,
|
||||
operations: operationHandlers,
|
||||
operations: operations.map((op: any) => op.name),
|
||||
scheduledJobs,
|
||||
description: constructor.__description,
|
||||
};
|
||||
|
||||
handlerRegistry.registerWithSchedule(config, serviceName);
|
||||
this.logger.info('Handler registered using decorator metadata', {
|
||||
handlerName,
|
||||
service: serviceName,
|
||||
operations: operations.map((op: any) => ({ name: op.name, method: op.method })),
|
||||
scheduledJobs: scheduledJobs.map((job: any) => ({
|
||||
operation: job.operation,
|
||||
cronPattern: job.cronPattern,
|
||||
immediately: job.immediately,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue