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,34 +1,34 @@
|
|||
/**
|
||||
* Service Container Setup for Data Pipeline
|
||||
* Configures dependency injection for the data pipeline service
|
||||
*/
|
||||
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { AppConfig } from '@stock-bot/config';
|
||||
|
||||
const logger = getLogger('data-pipeline-container');
|
||||
|
||||
/**
|
||||
* Configure the service container for data pipeline workloads
|
||||
*/
|
||||
export function setupServiceContainer(
|
||||
config: AppConfig,
|
||||
container: IServiceContainer
|
||||
): IServiceContainer {
|
||||
logger.info('Configuring data pipeline service container...');
|
||||
|
||||
// Data pipeline specific configuration
|
||||
// This service does more complex queries and transformations
|
||||
const poolSizes = {
|
||||
mongodb: config.environment === 'production' ? 40 : 20,
|
||||
postgres: config.environment === 'production' ? 50 : 25,
|
||||
cache: config.environment === 'production' ? 30 : 15,
|
||||
};
|
||||
|
||||
logger.info('Data pipeline pool sizes configured', poolSizes);
|
||||
|
||||
// The container is already configured with connections
|
||||
// Just return it with our logging
|
||||
return container;
|
||||
}
|
||||
/**
|
||||
* Service Container Setup for Data Pipeline
|
||||
* Configures dependency injection for the data pipeline service
|
||||
*/
|
||||
|
||||
import type { AppConfig } from '@stock-bot/config';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
const logger = getLogger('data-pipeline-container');
|
||||
|
||||
/**
|
||||
* Configure the service container for data pipeline workloads
|
||||
*/
|
||||
export function setupServiceContainer(
|
||||
config: AppConfig,
|
||||
container: IServiceContainer
|
||||
): IServiceContainer {
|
||||
logger.info('Configuring data pipeline service container...');
|
||||
|
||||
// Data pipeline specific configuration
|
||||
// This service does more complex queries and transformations
|
||||
const poolSizes = {
|
||||
mongodb: config.environment === 'production' ? 40 : 20,
|
||||
postgres: config.environment === 'production' ? 50 : 25,
|
||||
cache: config.environment === 'production' ? 30 : 15,
|
||||
};
|
||||
|
||||
logger.info('Data pipeline pool sizes configured', poolSizes);
|
||||
|
||||
// The container is already configured with connections
|
||||
// Just return it with our logging
|
||||
return container;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,111 +1,113 @@
|
|||
import {
|
||||
BaseHandler,
|
||||
Handler,
|
||||
Operation,
|
||||
ScheduledOperation,
|
||||
type IServiceContainer,
|
||||
} from '@stock-bot/handlers';
|
||||
import { clearPostgreSQLData } from './operations/clear-postgresql-data.operations';
|
||||
import { getSyncStatus } from './operations/enhanced-sync-status.operations';
|
||||
import { getExchangeStats } from './operations/exchange-stats.operations';
|
||||
import { getProviderMappingStats } from './operations/provider-mapping-stats.operations';
|
||||
import { syncQMExchanges } from './operations/qm-exchanges.operations';
|
||||
import { syncAllExchanges } from './operations/sync-all-exchanges.operations';
|
||||
import { syncIBExchanges } from './operations/sync-ib-exchanges.operations';
|
||||
import { syncQMProviderMappings } from './operations/sync-qm-provider-mappings.operations';
|
||||
|
||||
@Handler('exchanges')
|
||||
class ExchangesHandler extends BaseHandler {
|
||||
constructor(services: IServiceContainer) {
|
||||
super(services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync all exchanges - weekly full sync
|
||||
*/
|
||||
@Operation('sync-all-exchanges')
|
||||
@ScheduledOperation('sync-all-exchanges', '0 0 * * 0', {
|
||||
priority: 10,
|
||||
description: 'Weekly full exchange sync on Sunday at midnight',
|
||||
})
|
||||
async syncAllExchanges(payload?: { clearFirst?: boolean }): Promise<unknown> {
|
||||
const finalPayload = payload || { clearFirst: true };
|
||||
this.log('info', 'Starting sync of all exchanges', finalPayload);
|
||||
return syncAllExchanges(finalPayload, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync exchanges from QuestionsAndMethods
|
||||
*/
|
||||
@Operation('sync-qm-exchanges')
|
||||
@ScheduledOperation('sync-qm-exchanges', '0 1 * * *', {
|
||||
priority: 5,
|
||||
description: 'Daily sync of QM exchanges at 1 AM',
|
||||
})
|
||||
async syncQMExchanges(): Promise<unknown> {
|
||||
this.log('info', 'Starting QM exchanges sync...');
|
||||
return syncQMExchanges({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync exchanges from Interactive Brokers
|
||||
*/
|
||||
@Operation('sync-ib-exchanges')
|
||||
@ScheduledOperation('sync-ib-exchanges', '0 3 * * *', {
|
||||
priority: 3,
|
||||
description: 'Daily sync of IB exchanges at 3 AM',
|
||||
})
|
||||
async syncIBExchanges(): Promise<unknown> {
|
||||
this.log('info', 'Starting IB exchanges sync...');
|
||||
return syncIBExchanges({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync provider mappings from QuestionsAndMethods
|
||||
*/
|
||||
@Operation('sync-qm-provider-mappings')
|
||||
@ScheduledOperation('sync-qm-provider-mappings', '0 3 * * *', {
|
||||
priority: 7,
|
||||
description: 'Daily sync of QM provider mappings at 3 AM',
|
||||
})
|
||||
async syncQMProviderMappings(): Promise<unknown> {
|
||||
this.log('info', 'Starting QM provider mappings sync...');
|
||||
return syncQMProviderMappings({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear PostgreSQL data - maintenance operation
|
||||
*/
|
||||
@Operation('clear-postgresql-data')
|
||||
async clearPostgreSQLData(payload: { type?: 'exchanges' | 'provider_mappings' | 'all' }): Promise<unknown> {
|
||||
this.log('warn', 'Clearing PostgreSQL data', payload);
|
||||
return clearPostgreSQLData(payload, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exchange statistics
|
||||
*/
|
||||
@Operation('get-exchange-stats')
|
||||
async getExchangeStats(): Promise<unknown> {
|
||||
this.log('info', 'Getting exchange statistics...');
|
||||
return getExchangeStats({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get provider mapping statistics
|
||||
*/
|
||||
@Operation('get-provider-mapping-stats')
|
||||
async getProviderMappingStats(): Promise<unknown> {
|
||||
this.log('info', 'Getting provider mapping statistics...');
|
||||
return getProviderMappingStats({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enhanced sync status
|
||||
*/
|
||||
@Operation('enhanced-sync-status')
|
||||
async getEnhancedSyncStatus(): Promise<unknown> {
|
||||
this.log('info', 'Getting enhanced sync status...');
|
||||
return getSyncStatus({}, this.services);
|
||||
}
|
||||
}
|
||||
import {
|
||||
BaseHandler,
|
||||
Handler,
|
||||
Operation,
|
||||
ScheduledOperation,
|
||||
} from '@stock-bot/handlers';
|
||||
import type { IServiceContainer } from '@stock-bot/types';
|
||||
import { clearPostgreSQLData } from './operations/clear-postgresql-data.operations';
|
||||
import { getSyncStatus } from './operations/enhanced-sync-status.operations';
|
||||
import { getExchangeStats } from './operations/exchange-stats.operations';
|
||||
import { getProviderMappingStats } from './operations/provider-mapping-stats.operations';
|
||||
import { syncQMExchanges } from './operations/qm-exchanges.operations';
|
||||
import { syncAllExchanges } from './operations/sync-all-exchanges.operations';
|
||||
import { syncIBExchanges } from './operations/sync-ib-exchanges.operations';
|
||||
import { syncQMProviderMappings } from './operations/sync-qm-provider-mappings.operations';
|
||||
|
||||
@Handler('exchanges')
|
||||
class ExchangesHandler extends BaseHandler {
|
||||
constructor(services: IServiceContainer) {
|
||||
super(services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync all exchanges - weekly full sync
|
||||
*/
|
||||
@Operation('sync-all-exchanges')
|
||||
@ScheduledOperation('sync-all-exchanges', '0 0 * * 0', {
|
||||
priority: 10,
|
||||
description: 'Weekly full exchange sync on Sunday at midnight',
|
||||
})
|
||||
async syncAllExchanges(payload?: { clearFirst?: boolean }): Promise<unknown> {
|
||||
const finalPayload = payload || { clearFirst: true };
|
||||
this.log('info', 'Starting sync of all exchanges', finalPayload);
|
||||
return syncAllExchanges(finalPayload, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync exchanges from QuestionsAndMethods
|
||||
*/
|
||||
@Operation('sync-qm-exchanges')
|
||||
@ScheduledOperation('sync-qm-exchanges', '0 1 * * *', {
|
||||
priority: 5,
|
||||
description: 'Daily sync of QM exchanges at 1 AM',
|
||||
})
|
||||
async syncQMExchanges(): Promise<unknown> {
|
||||
this.log('info', 'Starting QM exchanges sync...');
|
||||
return syncQMExchanges({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync exchanges from Interactive Brokers
|
||||
*/
|
||||
@Operation('sync-ib-exchanges')
|
||||
@ScheduledOperation('sync-ib-exchanges', '0 3 * * *', {
|
||||
priority: 3,
|
||||
description: 'Daily sync of IB exchanges at 3 AM',
|
||||
})
|
||||
async syncIBExchanges(): Promise<unknown> {
|
||||
this.log('info', 'Starting IB exchanges sync...');
|
||||
return syncIBExchanges({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync provider mappings from QuestionsAndMethods
|
||||
*/
|
||||
@Operation('sync-qm-provider-mappings')
|
||||
@ScheduledOperation('sync-qm-provider-mappings', '0 3 * * *', {
|
||||
priority: 7,
|
||||
description: 'Daily sync of QM provider mappings at 3 AM',
|
||||
})
|
||||
async syncQMProviderMappings(): Promise<unknown> {
|
||||
this.log('info', 'Starting QM provider mappings sync...');
|
||||
return syncQMProviderMappings({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear PostgreSQL data - maintenance operation
|
||||
*/
|
||||
@Operation('clear-postgresql-data')
|
||||
async clearPostgreSQLData(payload: {
|
||||
type?: 'exchanges' | 'provider_mappings' | 'all';
|
||||
}): Promise<unknown> {
|
||||
this.log('warn', 'Clearing PostgreSQL data', payload);
|
||||
return clearPostgreSQLData(payload, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exchange statistics
|
||||
*/
|
||||
@Operation('get-exchange-stats')
|
||||
async getExchangeStats(): Promise<unknown> {
|
||||
this.log('info', 'Getting exchange statistics...');
|
||||
return getExchangeStats({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get provider mapping statistics
|
||||
*/
|
||||
@Operation('get-provider-mapping-stats')
|
||||
async getProviderMappingStats(): Promise<unknown> {
|
||||
this.log('info', 'Getting provider mapping statistics...');
|
||||
return getProviderMappingStats({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enhanced sync status
|
||||
*/
|
||||
@Operation('enhanced-sync-status')
|
||||
async getEnhancedSyncStatus(): Promise<unknown> {
|
||||
this.log('info', 'Getting enhanced sync status...');
|
||||
return getSyncStatus({}, this.services);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-clear-postgresql-data');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload, SyncStatus } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-status');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-exchange-stats');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-provider-mapping-stats');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { IServiceContainer } from '@stock-bot/types';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('sync-qm-exchanges');
|
||||
|
|
@ -62,7 +62,10 @@ interface Exchange {
|
|||
visible: boolean;
|
||||
}
|
||||
|
||||
async function findExchange(exchangeCode: string, postgresClient: IServiceContainer['postgres']): Promise<Exchange | null> {
|
||||
async function findExchange(
|
||||
exchangeCode: string,
|
||||
postgresClient: IServiceContainer['postgres']
|
||||
): Promise<Exchange | null> {
|
||||
const query = 'SELECT * FROM exchanges WHERE code = $1';
|
||||
const result = await postgresClient.query(query, [exchangeCode]);
|
||||
return result.rows[0] || null;
|
||||
|
|
@ -76,7 +79,10 @@ interface QMExchange {
|
|||
countryCode?: string;
|
||||
}
|
||||
|
||||
async function createExchange(qmExchange: QMExchange, postgresClient: IServiceContainer['postgres']): Promise<void> {
|
||||
async function createExchange(
|
||||
qmExchange: QMExchange,
|
||||
postgresClient: IServiceContainer['postgres']
|
||||
): Promise<void> {
|
||||
const query = `
|
||||
INSERT INTO exchanges (code, name, country, currency, visible)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload, SyncResult } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-all-exchanges');
|
||||
|
||||
export async function syncAllExchanges(payload: JobPayload, container: IServiceContainer): Promise<SyncResult> {
|
||||
export async function syncAllExchanges(
|
||||
payload: JobPayload,
|
||||
container: IServiceContainer
|
||||
): Promise<SyncResult> {
|
||||
const clearFirst = payload.clearFirst || true;
|
||||
logger.info('Starting comprehensive exchange sync...', { clearFirst });
|
||||
|
||||
|
|
@ -50,7 +53,6 @@ export async function syncAllExchanges(payload: JobPayload, container: IServiceC
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
async function clearPostgreSQLData(postgresClient: any): Promise<void> {
|
||||
logger.info('Clearing existing PostgreSQL data...');
|
||||
|
||||
|
|
@ -141,7 +143,11 @@ async function createProviderExchangeMapping(
|
|||
const postgresClient = container.postgres;
|
||||
|
||||
// Check if mapping already exists
|
||||
const existingMapping = await findProviderExchangeMapping(provider, providerExchangeCode, container);
|
||||
const existingMapping = await findProviderExchangeMapping(
|
||||
provider,
|
||||
providerExchangeCode,
|
||||
container
|
||||
);
|
||||
if (existingMapping) {
|
||||
// Don't override existing mappings to preserve manual work
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { MasterExchange } from '@stock-bot/mongodb';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('sync-ib-exchanges');
|
||||
|
|
@ -65,7 +65,10 @@ export async function syncIBExchanges(
|
|||
/**
|
||||
* Create or update master exchange record 1:1 from IB exchange
|
||||
*/
|
||||
async function createOrUpdateMasterExchange(ibExchange: IBExchange, container: IServiceContainer): Promise<void> {
|
||||
async function createOrUpdateMasterExchange(
|
||||
ibExchange: IBExchange,
|
||||
container: IServiceContainer
|
||||
): Promise<void> {
|
||||
const mongoClient = container.mongodb;
|
||||
const db = mongoClient.getDatabase();
|
||||
const collection = db.collection<MasterExchange>('masterExchanges');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload, SyncResult } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-qm-provider-mappings');
|
||||
|
|
@ -86,7 +86,6 @@ export async function syncQMProviderMappings(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
async function createProviderExchangeMapping(
|
||||
provider: string,
|
||||
providerExchangeCode: string,
|
||||
|
|
@ -103,7 +102,11 @@ async function createProviderExchangeMapping(
|
|||
const postgresClient = container.postgres;
|
||||
|
||||
// Check if mapping already exists
|
||||
const existingMapping = await findProviderExchangeMapping(provider, providerExchangeCode, container);
|
||||
const existingMapping = await findProviderExchangeMapping(
|
||||
provider,
|
||||
providerExchangeCode,
|
||||
container
|
||||
);
|
||||
if (existingMapping) {
|
||||
// Don't override existing mappings to preserve manual work
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,42 +1,41 @@
|
|||
/**
|
||||
* Handler auto-registration for data pipeline service
|
||||
* Automatically discovers and registers all handlers
|
||||
*/
|
||||
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { autoRegisterHandlers } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
// Import handlers for bundling (ensures they're included in the build)
|
||||
import './exchanges/exchanges.handler';
|
||||
import './symbols/symbols.handler';
|
||||
|
||||
const logger = getLogger('pipeline-handler-init');
|
||||
|
||||
/**
|
||||
* Initialize and register all handlers automatically
|
||||
*/
|
||||
export async function initializeAllHandlers(container: IServiceContainer): Promise<void> {
|
||||
logger.info('Initializing data pipeline handlers...');
|
||||
|
||||
try {
|
||||
// Auto-register all handlers in this directory
|
||||
const result = await autoRegisterHandlers(__dirname, container, {
|
||||
pattern: '.handler.',
|
||||
exclude: ['test', 'spec', '.old'],
|
||||
dryRun: false,
|
||||
});
|
||||
|
||||
logger.info('Handler auto-registration complete', {
|
||||
registered: result.registered,
|
||||
failed: result.failed,
|
||||
});
|
||||
|
||||
if (result.failed.length > 0) {
|
||||
logger.error('Some handlers failed to register', { failed: result.failed });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Handler auto-registration failed', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handler auto-registration for data pipeline service
|
||||
* Automatically discovers and registers all handlers
|
||||
*/
|
||||
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { autoRegisterHandlers } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
// Import handlers for bundling (ensures they're included in the build)
|
||||
import './exchanges/exchanges.handler';
|
||||
import './symbols/symbols.handler';
|
||||
|
||||
const logger = getLogger('pipeline-handler-init');
|
||||
|
||||
/**
|
||||
* Initialize and register all handlers automatically
|
||||
*/
|
||||
export async function initializeAllHandlers(container: IServiceContainer): Promise<void> {
|
||||
logger.info('Initializing data pipeline handlers...');
|
||||
|
||||
try {
|
||||
// Auto-register all handlers in this directory
|
||||
const result = await autoRegisterHandlers(__dirname, container, {
|
||||
pattern: '.handler.',
|
||||
exclude: ['test', 'spec', '.old'],
|
||||
dryRun: false,
|
||||
});
|
||||
|
||||
logger.info('Handler auto-registration complete', {
|
||||
registered: result.registered,
|
||||
failed: result.failed,
|
||||
});
|
||||
|
||||
if (result.failed.length > 0) {
|
||||
logger.error('Some handlers failed to register', { failed: result.failed });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Handler auto-registration failed', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('sync-qm-symbols');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('sync-status');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobPayload, SyncResult } from '../../../types/job-payloads';
|
||||
|
||||
const logger = getLogger('enhanced-sync-symbols-from-provider');
|
||||
|
|
@ -104,7 +104,11 @@ async function processSingleSymbol(
|
|||
}
|
||||
|
||||
// Find active provider exchange mapping
|
||||
const providerMapping = await findActiveProviderExchangeMapping(provider, exchangeCode, container);
|
||||
const providerMapping = await findActiveProviderExchangeMapping(
|
||||
provider,
|
||||
exchangeCode,
|
||||
container
|
||||
);
|
||||
|
||||
if (!providerMapping) {
|
||||
result.skipped++;
|
||||
|
|
@ -145,14 +149,22 @@ async function findActiveProviderExchangeMapping(
|
|||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
async function findSymbolByCodeAndExchange(symbol: string, exchangeId: string, container: IServiceContainer): Promise<any> {
|
||||
async function findSymbolByCodeAndExchange(
|
||||
symbol: string,
|
||||
exchangeId: string,
|
||||
container: IServiceContainer
|
||||
): Promise<any> {
|
||||
const postgresClient = container.postgres;
|
||||
const query = 'SELECT * FROM symbols WHERE symbol = $1 AND exchange_id = $2';
|
||||
const result = await postgresClient.query(query, [symbol, exchangeId]);
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
async function createSymbol(symbol: any, exchangeId: string, container: IServiceContainer): Promise<string> {
|
||||
async function createSymbol(
|
||||
symbol: any,
|
||||
exchangeId: string,
|
||||
container: IServiceContainer
|
||||
): Promise<string> {
|
||||
const postgresClient = container.postgres;
|
||||
const query = `
|
||||
INSERT INTO symbols (symbol, exchange_id, company_name, country, currency)
|
||||
|
|
@ -171,7 +183,11 @@ async function createSymbol(symbol: any, exchangeId: string, container: IService
|
|||
return result.rows[0].id;
|
||||
}
|
||||
|
||||
async function updateSymbol(symbolId: string, symbol: any, container: IServiceContainer): Promise<void> {
|
||||
async function updateSymbol(
|
||||
symbolId: string,
|
||||
symbol: any,
|
||||
container: IServiceContainer
|
||||
): Promise<void> {
|
||||
const postgresClient = container.postgres;
|
||||
const query = `
|
||||
UPDATE symbols
|
||||
|
|
|
|||
|
|
@ -1,68 +1,71 @@
|
|||
import {
|
||||
BaseHandler,
|
||||
Handler,
|
||||
Operation,
|
||||
ScheduledOperation,
|
||||
type IServiceContainer,
|
||||
} from '@stock-bot/handlers';
|
||||
import { syncQMSymbols } from './operations/qm-symbols.operations';
|
||||
import { syncSymbolsFromProvider } from './operations/sync-symbols-from-provider.operations';
|
||||
import { getSyncStatus } from './operations/sync-status.operations';
|
||||
|
||||
@Handler('symbols')
|
||||
class SymbolsHandler extends BaseHandler {
|
||||
constructor(services: IServiceContainer) {
|
||||
super(services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync symbols from QuestionsAndMethods API
|
||||
*/
|
||||
@ScheduledOperation('sync-qm-symbols', '0 2 * * *', {
|
||||
priority: 5,
|
||||
description: 'Daily sync of QM symbols at 2 AM',
|
||||
})
|
||||
async syncQMSymbols(): Promise<{ processed: number; created: number; updated: number }> {
|
||||
this.log('info', 'Starting QM symbols sync...');
|
||||
return syncQMSymbols({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync symbols from specific provider
|
||||
*/
|
||||
@Operation('sync-symbols-qm')
|
||||
@ScheduledOperation('sync-symbols-qm', '0 4 * * *', {
|
||||
priority: 5,
|
||||
description: 'Daily sync of symbols from QM provider at 4 AM',
|
||||
})
|
||||
async syncSymbolsQM(): Promise<unknown> {
|
||||
return this.syncSymbolsFromProvider({ provider: 'qm', clearFirst: false });
|
||||
}
|
||||
|
||||
@Operation('sync-symbols-eod')
|
||||
async syncSymbolsEOD(payload: { provider: string; clearFirst?: boolean }): Promise<unknown> {
|
||||
return this.syncSymbolsFromProvider({ ...payload, provider: 'eod' });
|
||||
}
|
||||
|
||||
@Operation('sync-symbols-ib')
|
||||
async syncSymbolsIB(payload: { provider: string; clearFirst?: boolean }): Promise<unknown> {
|
||||
return this.syncSymbolsFromProvider({ ...payload, provider: 'ib' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sync status for symbols
|
||||
*/
|
||||
@Operation('sync-status')
|
||||
async getSyncStatus(): Promise<unknown> {
|
||||
this.log('info', 'Getting symbol sync status...');
|
||||
return getSyncStatus({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to sync symbols from a provider
|
||||
*/
|
||||
private async syncSymbolsFromProvider(payload: { provider: string; clearFirst?: boolean }): Promise<unknown> {
|
||||
this.log('info', 'Syncing symbols from provider', { provider: payload.provider });
|
||||
return syncSymbolsFromProvider(payload, this.services);
|
||||
}
|
||||
}
|
||||
import {
|
||||
BaseHandler,
|
||||
Handler,
|
||||
Operation,
|
||||
ScheduledOperation,
|
||||
type IServiceContainer,
|
||||
} from '@stock-bot/handlers';
|
||||
import { syncQMSymbols } from './operations/qm-symbols.operations';
|
||||
import { getSyncStatus } from './operations/sync-status.operations';
|
||||
import { syncSymbolsFromProvider } from './operations/sync-symbols-from-provider.operations';
|
||||
|
||||
@Handler('symbols')
|
||||
class SymbolsHandler extends BaseHandler {
|
||||
constructor(services: IServiceContainer) {
|
||||
super(services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync symbols from QuestionsAndMethods API
|
||||
*/
|
||||
@ScheduledOperation('sync-qm-symbols', '0 2 * * *', {
|
||||
priority: 5,
|
||||
description: 'Daily sync of QM symbols at 2 AM',
|
||||
})
|
||||
async syncQMSymbols(): Promise<{ processed: number; created: number; updated: number }> {
|
||||
this.log('info', 'Starting QM symbols sync...');
|
||||
return syncQMSymbols({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync symbols from specific provider
|
||||
*/
|
||||
@Operation('sync-symbols-qm')
|
||||
@ScheduledOperation('sync-symbols-qm', '0 4 * * *', {
|
||||
priority: 5,
|
||||
description: 'Daily sync of symbols from QM provider at 4 AM',
|
||||
})
|
||||
async syncSymbolsQM(): Promise<unknown> {
|
||||
return this.syncSymbolsFromProvider({ provider: 'qm', clearFirst: false });
|
||||
}
|
||||
|
||||
@Operation('sync-symbols-eod')
|
||||
async syncSymbolsEOD(payload: { provider: string; clearFirst?: boolean }): Promise<unknown> {
|
||||
return this.syncSymbolsFromProvider({ ...payload, provider: 'eod' });
|
||||
}
|
||||
|
||||
@Operation('sync-symbols-ib')
|
||||
async syncSymbolsIB(payload: { provider: string; clearFirst?: boolean }): Promise<unknown> {
|
||||
return this.syncSymbolsFromProvider({ ...payload, provider: 'ib' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sync status for symbols
|
||||
*/
|
||||
@Operation('sync-status')
|
||||
async getSyncStatus(): Promise<unknown> {
|
||||
this.log('info', 'Getting symbol sync status...');
|
||||
return getSyncStatus({}, this.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to sync symbols from a provider
|
||||
*/
|
||||
private async syncSymbolsFromProvider(payload: {
|
||||
provider: string;
|
||||
clearFirst?: boolean;
|
||||
}): Promise<unknown> {
|
||||
this.log('info', 'Syncing symbols from provider', { provider: payload.provider });
|
||||
return syncSymbolsFromProvider(payload, this.services);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@
|
|||
* Simplified entry point using ServiceApplication framework
|
||||
*/
|
||||
|
||||
import { initializeStockConfig } from '@stock-bot/stock-config';
|
||||
import { ServiceApplication } from '@stock-bot/di';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
// Local imports
|
||||
import { initializeAllHandlers } from './handlers';
|
||||
import { initializeStockConfig } from '@stock-bot/stock-config';
|
||||
import { createRoutes } from './routes/create-routes';
|
||||
import { setupServiceContainer } from './container-setup';
|
||||
// Local imports
|
||||
import { initializeAllHandlers } from './handlers';
|
||||
|
||||
// Initialize configuration with service-specific overrides
|
||||
const config = initializeStockConfig('dataPipeline');
|
||||
|
|
@ -43,12 +42,12 @@ const app = new ServiceApplication(
|
|||
},
|
||||
{
|
||||
// Custom lifecycle hooks
|
||||
onContainerReady: (container) => {
|
||||
onContainerReady: container => {
|
||||
// Setup service-specific configuration
|
||||
const enhancedContainer = setupServiceContainer(config, container);
|
||||
return enhancedContainer;
|
||||
},
|
||||
onStarted: (_port) => {
|
||||
onStarted: _port => {
|
||||
const logger = getLogger('data-pipeline');
|
||||
logger.info('Data pipeline service startup initiated with ServiceApplication framework');
|
||||
},
|
||||
|
|
@ -59,7 +58,7 @@ const app = new ServiceApplication(
|
|||
async function createContainer(config: any) {
|
||||
const { ServiceContainerBuilder } = await import('@stock-bot/di');
|
||||
const builder = new ServiceContainerBuilder();
|
||||
|
||||
|
||||
const container = await builder
|
||||
.withConfig(config)
|
||||
.withOptions({
|
||||
|
|
@ -74,7 +73,7 @@ async function createContainer(config: any) {
|
|||
skipInitialization: false, // Let builder handle initialization
|
||||
})
|
||||
.build();
|
||||
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
|
@ -83,4 +82,4 @@ app.start(createContainer, createRoutes, initializeAllHandlers).catch(error => {
|
|||
const logger = getLogger('data-pipeline');
|
||||
logger.fatal('Failed to start data pipeline service', { error });
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
/**
|
||||
* Route factory for data pipeline service
|
||||
* Creates routes with access to the service container
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { healthRoutes } from './health.routes';
|
||||
import { createSyncRoutes } from './sync.routes';
|
||||
import { createEnhancedSyncRoutes } from './enhanced-sync.routes';
|
||||
import { createStatsRoutes } from './stats.routes';
|
||||
|
||||
export function createRoutes(container: IServiceContainer): Hono {
|
||||
const app = new Hono();
|
||||
|
||||
// Add container to context for all routes
|
||||
app.use('*', async (c, next) => {
|
||||
c.set('container', container);
|
||||
await next();
|
||||
});
|
||||
|
||||
// Mount routes
|
||||
app.route('/health', healthRoutes);
|
||||
app.route('/sync', createSyncRoutes(container));
|
||||
app.route('/sync', createEnhancedSyncRoutes(container));
|
||||
app.route('/sync/stats', createStatsRoutes(container));
|
||||
|
||||
return app;
|
||||
}
|
||||
/**
|
||||
* Route factory for data pipeline service
|
||||
* Creates routes with access to the service container
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { createEnhancedSyncRoutes } from './enhanced-sync.routes';
|
||||
import { healthRoutes } from './health.routes';
|
||||
import { createStatsRoutes } from './stats.routes';
|
||||
import { createSyncRoutes } from './sync.routes';
|
||||
|
||||
export function createRoutes(container: IServiceContainer): Hono {
|
||||
const app = new Hono();
|
||||
|
||||
// Add container to context for all routes
|
||||
app.use('*', async (c, next) => {
|
||||
c.set('container', container);
|
||||
await next();
|
||||
});
|
||||
|
||||
// Mount routes
|
||||
app.route('/health', healthRoutes);
|
||||
app.route('/sync', createSyncRoutes(container));
|
||||
app.route('/sync', createEnhancedSyncRoutes(container));
|
||||
app.route('/sync/stats', createStatsRoutes(container));
|
||||
|
||||
return app;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Hono } from 'hono';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
const logger = getLogger('enhanced-sync-routes');
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ export function createEnhancedSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('sync-all-exchanges', {
|
||||
|
|
@ -40,7 +40,7 @@ export function createEnhancedSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('sync-qm-provider-mappings', {
|
||||
|
|
@ -69,7 +69,7 @@ export function createEnhancedSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('sync-ib-exchanges', {
|
||||
|
|
@ -98,7 +98,7 @@ export function createEnhancedSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const symbolsQueue = queueManager.getQueue('symbols');
|
||||
|
||||
const job = await symbolsQueue.addJob('sync-status', {
|
||||
|
|
@ -124,7 +124,7 @@ export function createEnhancedSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('clear-postgresql-data', {
|
||||
|
|
@ -148,4 +148,4 @@ export function createEnhancedSyncRoutes(container: IServiceContainer) {
|
|||
});
|
||||
|
||||
return enhancedSync;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Hono } from 'hono';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
const logger = getLogger('stats-routes');
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ export function createStatsRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('get-exchange-stats', {
|
||||
|
|
@ -38,7 +38,7 @@ export function createStatsRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('get-provider-mapping-stats', {
|
||||
|
|
@ -57,4 +57,4 @@ export function createStatsRoutes(container: IServiceContainer) {
|
|||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Hono } from 'hono';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
const logger = getLogger('sync-routes');
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ export function createSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const symbolsQueue = queueManager.getQueue('symbols');
|
||||
|
||||
const job = await symbolsQueue.addJob('sync-qm-symbols', {
|
||||
|
|
@ -39,7 +39,7 @@ export function createSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const exchangesQueue = queueManager.getQueue('exchanges');
|
||||
|
||||
const job = await exchangesQueue.addJob('sync-qm-exchanges', {
|
||||
|
|
@ -65,7 +65,7 @@ export function createSyncRoutes(container: IServiceContainer) {
|
|||
if (!queueManager) {
|
||||
return c.json({ success: false, error: 'Queue manager not available' }, 503);
|
||||
}
|
||||
|
||||
|
||||
const symbolsQueue = queueManager.getQueue('symbols');
|
||||
|
||||
const job = await symbolsQueue.addJob('sync-symbols-from-provider', {
|
||||
|
|
@ -89,4 +89,4 @@ export function createSyncRoutes(container: IServiceContainer) {
|
|||
});
|
||||
|
||||
return sync;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue