diff --git a/.serena/cache/typescript/document_symbols_cache_v20-05-25.pkl b/.serena/cache/typescript/document_symbols_cache_v20-05-25.pkl index 937683c..ae8f4ce 100644 Binary files a/.serena/cache/typescript/document_symbols_cache_v20-05-25.pkl and b/.serena/cache/typescript/document_symbols_cache_v20-05-25.pkl differ diff --git a/apps/data-pipeline/src/container-setup.ts b/apps/data-pipeline/src/container-setup.ts index a1f2638..1482d04 100644 --- a/apps/data-pipeline/src/container-setup.ts +++ b/apps/data-pipeline/src/container-setup.ts @@ -3,7 +3,7 @@ * Configures dependency injection for the data pipeline service */ -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import { getLogger } from '@stock-bot/logger'; import type { AppConfig } from '@stock-bot/config'; @@ -14,8 +14,8 @@ const logger = getLogger('data-pipeline-container'); */ export function setupServiceContainer( config: AppConfig, - container: ServiceContainer -): ServiceContainer { + container: IServiceContainer +): IServiceContainer { logger.info('Configuring data pipeline service container...'); // Data pipeline specific configuration diff --git a/apps/data-pipeline/src/handlers/exchanges/exchanges.handler.ts b/apps/data-pipeline/src/handlers/exchanges/exchanges.handler.ts index cd503c3..55a1c18 100644 --- a/apps/data-pipeline/src/handlers/exchanges/exchanges.handler.ts +++ b/apps/data-pipeline/src/handlers/exchanges/exchanges.handler.ts @@ -1,6 +1,6 @@ import { getLogger } from '@stock-bot/logger'; import { handlerRegistry, createJobHandler, type HandlerConfig, type ScheduledJobConfig } from '@stock-bot/queue'; -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import { exchangeOperations } from './operations'; const logger = getLogger('exchanges-handler'); @@ -52,7 +52,7 @@ const exchangesHandlerConfig: HandlerConfig = { }, }; -export function initializeExchangesHandler(container: ServiceContainer) { +export function initializeExchangesHandler(container: IServiceContainer) { logger.info('Registering exchanges handler...'); // Update operations to use container diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/clear-postgresql-data.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/clear-postgresql-data.operations.ts index b47b0cb..733cd35 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/clear-postgresql-data.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/clear-postgresql-data.operations.ts @@ -1,12 +1,12 @@ import { getLogger } from '@stock-bot/logger'; -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload } from '../../../types/job-payloads'; const logger = getLogger('enhanced-sync-clear-postgresql-data'); export async function clearPostgreSQLData( payload: JobPayload, - container: ServiceContainer + container: IServiceContainer ): Promise<{ exchangesCleared: number; symbolsCleared: number; diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/enhanced-sync-status.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/enhanced-sync-status.operations.ts index f1ab881..96e5ad1 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/enhanced-sync-status.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/enhanced-sync-status.operations.ts @@ -1,12 +1,12 @@ import { getLogger } from '@stock-bot/logger'; -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload, SyncStatus } from '../../../types/job-payloads'; const logger = getLogger('enhanced-sync-status'); export async function getSyncStatus( payload: JobPayload, - container: ServiceContainer + container: IServiceContainer ): Promise { logger.info('Getting comprehensive sync status...'); diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/exchange-stats.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/exchange-stats.operations.ts index fdc17fc..eeb6c59 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/exchange-stats.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/exchange-stats.operations.ts @@ -1,12 +1,12 @@ import { getLogger } from '@stock-bot/logger'; -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload } from '../../../types/job-payloads'; const logger = getLogger('enhanced-sync-exchange-stats'); export async function getExchangeStats( payload: JobPayload, - container: ServiceContainer + container: IServiceContainer ): Promise { logger.info('Getting exchange statistics...'); diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/provider-mapping-stats.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/provider-mapping-stats.operations.ts index 9d07412..3a1381a 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/provider-mapping-stats.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/provider-mapping-stats.operations.ts @@ -1,12 +1,12 @@ import { getLogger } from '@stock-bot/logger'; -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload } from '../../../types/job-payloads'; const logger = getLogger('enhanced-sync-provider-mapping-stats'); export async function getProviderMappingStats( payload: JobPayload, - container: ServiceContainer + container: IServiceContainer ): Promise { logger.info('Getting provider mapping statistics...'); diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/qm-exchanges.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/qm-exchanges.operations.ts index cebea42..e7761cd 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/qm-exchanges.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/qm-exchanges.operations.ts @@ -1,17 +1,19 @@ import { getLogger } from '@stock-bot/logger'; import { getMongoDBClient, getPostgreSQLClient } from '../../../clients'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload } from '../../../types/job-payloads'; const logger = getLogger('sync-qm-exchanges'); export async function syncQMExchanges( - payload: JobPayload + payload: JobPayload, + container: IServiceContainer ): Promise<{ processed: number; created: number; updated: number }> { logger.info('Starting QM exchanges sync...'); try { - const mongoClient = getMongoDBClient(); - const postgresClient = getPostgreSQLClient(); + const mongoClient = container.mongodb; + const postgresClient = container.postgres; // 1. Get all QM exchanges from MongoDB const qmExchanges = await mongoClient.find('qmExchanges', {}); diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/sync-all-exchanges.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/sync-all-exchanges.operations.ts index 7c642b4..9dbfd57 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/sync-all-exchanges.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/sync-all-exchanges.operations.ts @@ -1,10 +1,10 @@ import { getLogger } from '@stock-bot/logger'; -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload, SyncResult } from '../../../types/job-payloads'; const logger = getLogger('enhanced-sync-all-exchanges'); -export async function syncAllExchanges(payload: JobPayload, container: ServiceContainer): Promise { +export async function syncAllExchanges(payload: JobPayload, container: IServiceContainer): Promise { const clearFirst = payload.clearFirst || true; logger.info('Starting comprehensive exchange sync...', { clearFirst }); @@ -67,7 +67,7 @@ async function clearPostgreSQLData(postgresClient: any): Promise { logger.info('PostgreSQL data cleared successfully'); } -async function syncEODExchanges(container: ServiceContainer): Promise { +async function syncEODExchanges(container: IServiceContainer): Promise { const mongoClient = container.mongodb; const exchanges = await mongoClient.find('eodExchanges', { active: true }); const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 }; @@ -96,7 +96,7 @@ async function syncEODExchanges(container: ServiceContainer): Promise { +async function syncIBExchanges(container: IServiceContainer): Promise { const mongoClient = container.mongodb; const exchanges = await mongoClient.find('ibExchanges', {}); const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 }; @@ -132,7 +132,7 @@ async function createProviderExchangeMapping( countryCode: string | null, currency: string | null, confidence: number, - container: ServiceContainer + container: IServiceContainer ): Promise { if (!providerExchangeCode) { return; @@ -181,7 +181,7 @@ async function findOrCreateMasterExchange( providerName: string, countryCode: string | null, currency: string | null, - container: ServiceContainer + container: IServiceContainer ): Promise { const postgresClient = container.postgres; @@ -237,7 +237,7 @@ function getBasicExchangeMapping(providerCode: string): string | null { async function findProviderExchangeMapping( provider: string, providerExchangeCode: string, - container: ServiceContainer + container: IServiceContainer ): Promise { const postgresClient = container.postgres; const query = @@ -246,7 +246,7 @@ async function findProviderExchangeMapping( return result.rows[0] || null; } -async function findExchangeByCode(code: string, container: ServiceContainer): Promise { +async function findExchangeByCode(code: string, container: IServiceContainer): Promise { const postgresClient = container.postgres; const query = 'SELECT * FROM exchanges WHERE code = $1'; const result = await postgresClient.query(query, [code]); diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/sync-ib-exchanges.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/sync-ib-exchanges.operations.ts index d8da00c..09be8f2 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/sync-ib-exchanges.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/sync-ib-exchanges.operations.ts @@ -1,6 +1,7 @@ import { getLogger } from '@stock-bot/logger'; import type { MasterExchange } from '@stock-bot/mongodb'; import { getMongoDBClient } from '../../../clients'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload } from '../../../types/job-payloads'; const logger = getLogger('sync-ib-exchanges'); @@ -15,12 +16,13 @@ interface IBExchange { } export async function syncIBExchanges( - payload: JobPayload + payload: JobPayload, + container: IServiceContainer ): Promise<{ syncedCount: number; totalExchanges: number }> { logger.info('Syncing IB exchanges from database...'); try { - const mongoClient = getMongoDBClient(); + const mongoClient = container.mongodb; const db = mongoClient.getDatabase(); // Filter by country code US and CA @@ -37,7 +39,7 @@ export async function syncIBExchanges( for (const exchange of ibExchanges) { try { - await createOrUpdateMasterExchange(exchange); + await createOrUpdateMasterExchange(exchange, container); syncedCount++; logger.debug('Synced IB exchange', { @@ -64,8 +66,8 @@ export async function syncIBExchanges( /** * Create or update master exchange record 1:1 from IB exchange */ -async function createOrUpdateMasterExchange(ibExchange: IBExchange): Promise { - const mongoClient = getMongoDBClient(); +async function createOrUpdateMasterExchange(ibExchange: IBExchange, container: IServiceContainer): Promise { + const mongoClient = container.mongodb; const db = mongoClient.getDatabase(); const collection = db.collection('masterExchanges'); diff --git a/apps/data-pipeline/src/handlers/exchanges/operations/sync-qm-provider-mappings.operations.ts b/apps/data-pipeline/src/handlers/exchanges/operations/sync-qm-provider-mappings.operations.ts index ad7900e..7c0bee4 100644 --- a/apps/data-pipeline/src/handlers/exchanges/operations/sync-qm-provider-mappings.operations.ts +++ b/apps/data-pipeline/src/handlers/exchanges/operations/sync-qm-provider-mappings.operations.ts @@ -1,10 +1,14 @@ import { getLogger } from '@stock-bot/logger'; import { getMongoDBClient, getPostgreSQLClient } from '../../../clients'; +import type { IServiceContainer } from '@stock-bot/handlers'; import type { JobPayload, SyncResult } from '../../../types/job-payloads'; const logger = getLogger('enhanced-sync-qm-provider-mappings'); -export async function syncQMProviderMappings(payload: JobPayload): Promise { +export async function syncQMProviderMappings( + payload: JobPayload, + container: IServiceContainer +): Promise { logger.info('Starting QM provider exchange mappings sync...'); const result: SyncResult = { @@ -16,8 +20,8 @@ export async function syncQMProviderMappings(payload: JobPayload): Promise { if (!providerExchangeCode) { return; } - const postgresClient = getPostgreSQLClient(); + const postgresClient = container.postgres; // Check if mapping already exists - const existingMapping = await findProviderExchangeMapping(provider, providerExchangeCode); + const existingMapping = await findProviderExchangeMapping(provider, providerExchangeCode, container); if (existingMapping) { // Don't override existing mappings to preserve manual work return; @@ -108,7 +115,8 @@ async function createProviderExchangeMapping( providerExchangeCode, providerExchangeName, countryCode, - currency + currency, + container ); // Create the provider exchange mapping @@ -133,9 +141,10 @@ async function createProviderExchangeMapping( async function findProviderExchangeMapping( provider: string, - providerExchangeCode: string + providerExchangeCode: string, + container: IServiceContainer ): Promise { - const postgresClient = getPostgreSQLClient(); + const postgresClient = container.postgres; const query = 'SELECT * FROM provider_exchange_mappings WHERE provider = $1 AND provider_exchange_code = $2'; const result = await postgresClient.query(query, [provider, providerExchangeCode]); @@ -146,12 +155,13 @@ async function findOrCreateMasterExchange( providerCode: string, providerName: string, countryCode: string | null, - currency: string | null + currency: string | null, + container: IServiceContainer ): Promise { - const postgresClient = getPostgreSQLClient(); + const postgresClient = container.postgres; // First, try to find exact match - let masterExchange = await findExchangeByCode(providerCode); + let masterExchange = await findExchangeByCode(providerCode, container); if (masterExchange) { return masterExchange; @@ -160,7 +170,7 @@ async function findOrCreateMasterExchange( // Try to find by similar codes (basic mapping) const basicMapping = getBasicExchangeMapping(providerCode); if (basicMapping) { - masterExchange = await findExchangeByCode(basicMapping); + masterExchange = await findExchangeByCode(basicMapping, container); if (masterExchange) { return masterExchange; } @@ -199,8 +209,8 @@ function getBasicExchangeMapping(providerCode: string): string | null { return mappings[providerCode.toUpperCase()] || null; } -async function findExchangeByCode(code: string): Promise { - const postgresClient = getPostgreSQLClient(); +async function findExchangeByCode(code: string, container: IServiceContainer): Promise { + const postgresClient = container.postgres; const query = 'SELECT * FROM exchanges WHERE code = $1'; const result = await postgresClient.query(query, [code]); return result.rows[0] || null; diff --git a/apps/web-api/src/container-setup.ts b/apps/web-api/src/container-setup.ts index 2e71f0f..2cec315 100644 --- a/apps/web-api/src/container-setup.ts +++ b/apps/web-api/src/container-setup.ts @@ -3,7 +3,7 @@ * Configures dependency injection for the web API service */ -import type { ServiceContainer } from '@stock-bot/di'; +import type { IServiceContainer } from '@stock-bot/handlers'; import { getLogger } from '@stock-bot/logger'; import type { AppConfig } from '@stock-bot/config'; @@ -14,8 +14,8 @@ const logger = getLogger('web-api-container'); */ export function setupServiceContainer( config: AppConfig, - container: ServiceContainer -): ServiceContainer { + container: IServiceContainer +): IServiceContainer { logger.info('Configuring web API service container...'); // Web API specific configuration diff --git a/apps/web-api/src/routes/create-routes.ts b/apps/web-api/src/routes/create-routes.ts index 11867ca..3411b9a 100644 --- a/apps/web-api/src/routes/create-routes.ts +++ b/apps/web-api/src/routes/create-routes.ts @@ -4,17 +4,15 @@ */ import { Hono } from 'hono'; -import type { ServiceContainer } from '@stock-bot/di'; -import { healthRoutes, exchangeRoutes } from './index'; +import type { IServiceContainer } from '@stock-bot/handlers'; +import { healthRoutes } from './health.routes'; +import { createExchangeRoutes } from './exchange.routes'; -export function createRoutes(container: ServiceContainer): Hono { +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(); - }); + // Create routes with container + const exchangeRoutes = createExchangeRoutes(container); // Mount routes app.route('/health', healthRoutes); diff --git a/apps/web-api/src/routes/exchange.routes.ts b/apps/web-api/src/routes/exchange.routes.ts index 666b411..fd33cf6 100644 --- a/apps/web-api/src/routes/exchange.routes.ts +++ b/apps/web-api/src/routes/exchange.routes.ts @@ -3,7 +3,8 @@ */ import { Hono } from 'hono'; import { getLogger } from '@stock-bot/logger'; -import { exchangeService } from '../services/exchange.service'; +import type { IServiceContainer } from '@stock-bot/handlers'; +import { createExchangeService } from '../services/exchange.service'; import { createSuccessResponse, handleError } from '../utils/error-handler'; import { validateCreateExchange, @@ -13,243 +14,249 @@ import { } from '../utils/validation'; const logger = getLogger('exchange-routes'); -export const exchangeRoutes = new Hono(); -// Get all exchanges with provider mapping counts and mappings -exchangeRoutes.get('/', async c => { - logger.debug('Getting all exchanges'); - try { - const exchanges = await exchangeService.getAllExchanges(); - logger.info('Successfully retrieved exchanges', { count: exchanges.length }); - return c.json(createSuccessResponse(exchanges, undefined, exchanges.length)); - } catch (error) { - logger.error('Failed to get exchanges', { error }); - return handleError(c, error, 'to get exchanges'); - } -}); +export function createExchangeRoutes(container: IServiceContainer) { + const exchangeRoutes = new Hono(); + const exchangeService = createExchangeService(container); -// Get exchange by ID with detailed provider mappings -exchangeRoutes.get('/:id', async c => { - const exchangeId = c.req.param('id'); - logger.debug('Getting exchange by ID', { exchangeId }); - - try { - const result = await exchangeService.getExchangeById(exchangeId); - - if (!result) { - logger.warn('Exchange not found', { exchangeId }); - return c.json(createSuccessResponse(null, 'Exchange not found'), 404); + // Get all exchanges with provider mapping counts and mappings + exchangeRoutes.get('/', async c => { + logger.debug('Getting all exchanges'); + try { + const exchanges = await exchangeService.getAllExchanges(); + logger.info('Successfully retrieved exchanges', { count: exchanges.length }); + return c.json(createSuccessResponse(exchanges, undefined, exchanges.length)); + } catch (error) { + logger.error('Failed to get exchanges', { error }); + return handleError(c, error, 'to get exchanges'); } + }); - logger.info('Successfully retrieved exchange details', { - exchangeId, - exchangeCode: result.exchange.code, - mappingCount: result.provider_mappings.length, - }); - return c.json(createSuccessResponse(result)); - } catch (error) { - logger.error('Failed to get exchange details', { error, exchangeId }); - return handleError(c, error, 'to get exchange details'); - } -}); + // Get exchange by ID with detailed provider mappings + exchangeRoutes.get('/:id', async c => { + const exchangeId = c.req.param('id'); + logger.debug('Getting exchange by ID', { exchangeId }); -// Create new exchange -exchangeRoutes.post('/', async c => { - logger.debug('Creating new exchange'); + try { + const result = await exchangeService.getExchangeById(exchangeId); - try { - const body = await c.req.json(); - logger.debug('Received exchange creation request', { requestBody: body }); + if (!result) { + logger.warn('Exchange not found', { exchangeId }); + return c.json(createSuccessResponse(null, 'Exchange not found'), 404); + } - const validatedData = validateCreateExchange(body); - logger.debug('Exchange data validated successfully', { validatedData }); - - const exchange = await exchangeService.createExchange(validatedData); - logger.info('Exchange created successfully', { - exchangeId: exchange.id, - code: exchange.code, - name: exchange.name, - }); - - return c.json(createSuccessResponse(exchange, 'Exchange created successfully'), 201); - } catch (error) { - logger.error('Failed to create exchange', { error }); - return handleError(c, error, 'to create exchange'); - } -}); - -// Update exchange (activate/deactivate, rename, etc.) -exchangeRoutes.patch('/:id', async c => { - const exchangeId = c.req.param('id'); - logger.debug('Updating exchange', { exchangeId }); - - try { - const body = await c.req.json(); - logger.debug('Received exchange update request', { exchangeId, updates: body }); - - const validatedUpdates = validateUpdateExchange(body); - logger.debug('Exchange update data validated', { exchangeId, validatedUpdates }); - - const exchange = await exchangeService.updateExchange(exchangeId, validatedUpdates); - - if (!exchange) { - logger.warn('Exchange not found for update', { exchangeId }); - return c.json(createSuccessResponse(null, 'Exchange not found'), 404); + logger.info('Successfully retrieved exchange details', { + exchangeId, + exchangeCode: result.exchange.code, + mappingCount: result.provider_mappings.length, + }); + return c.json(createSuccessResponse(result)); + } catch (error) { + logger.error('Failed to get exchange details', { error, exchangeId }); + return handleError(c, error, 'to get exchange details'); } + }); - logger.info('Exchange updated successfully', { - exchangeId, - code: exchange.code, - updates: validatedUpdates, - }); + // Create new exchange + exchangeRoutes.post('/', async c => { + logger.debug('Creating new exchange'); - // Log special actions - if (validatedUpdates.visible === false) { - logger.warn('Exchange marked as hidden - provider mappings will be deleted', { + try { + const body = await c.req.json(); + logger.debug('Received exchange creation request', { requestBody: body }); + + const validatedData = validateCreateExchange(body); + logger.debug('Exchange data validated successfully', { validatedData }); + + const exchange = await exchangeService.createExchange(validatedData); + logger.info('Exchange created successfully', { + exchangeId: exchange.id, + code: exchange.code, + name: exchange.name, + }); + + return c.json(createSuccessResponse(exchange, 'Exchange created successfully'), 201); + } catch (error) { + logger.error('Failed to create exchange', { error }); + return handleError(c, error, 'to create exchange'); + } + }); + + // Update exchange (activate/deactivate, rename, etc.) + exchangeRoutes.patch('/:id', async c => { + const exchangeId = c.req.param('id'); + logger.debug('Updating exchange', { exchangeId }); + + try { + const body = await c.req.json(); + logger.debug('Received exchange update request', { exchangeId, updates: body }); + + const validatedUpdates = validateUpdateExchange(body); + logger.debug('Exchange update data validated', { exchangeId, validatedUpdates }); + + const exchange = await exchangeService.updateExchange(exchangeId, validatedUpdates); + + if (!exchange) { + logger.warn('Exchange not found for update', { exchangeId }); + return c.json(createSuccessResponse(null, 'Exchange not found'), 404); + } + + logger.info('Exchange updated successfully', { exchangeId, code: exchange.code, + updates: validatedUpdates, }); + + // Log special actions + if (validatedUpdates.visible === false) { + logger.warn('Exchange marked as hidden - provider mappings will be deleted', { + exchangeId, + code: exchange.code, + }); + } + + return c.json(createSuccessResponse(exchange, 'Exchange updated successfully')); + } catch (error) { + logger.error('Failed to update exchange', { error, exchangeId }); + return handleError(c, error, 'to update exchange'); } + }); - return c.json(createSuccessResponse(exchange, 'Exchange updated successfully')); - } catch (error) { - logger.error('Failed to update exchange', { error, exchangeId }); - return handleError(c, error, 'to update exchange'); - } -}); + // Get all provider mappings + exchangeRoutes.get('/provider-mappings/all', async c => { + logger.debug('Getting all provider mappings'); -// Get all provider mappings -exchangeRoutes.get('/provider-mappings/all', async c => { - logger.debug('Getting all provider mappings'); - - try { - const mappings = await exchangeService.getAllProviderMappings(); - logger.info('Successfully retrieved all provider mappings', { count: mappings.length }); - return c.json(createSuccessResponse(mappings, undefined, mappings.length)); - } catch (error) { - logger.error('Failed to get provider mappings', { error }); - return handleError(c, error, 'to get provider mappings'); - } -}); - -// Get provider mappings by provider -exchangeRoutes.get('/provider-mappings/:provider', async c => { - const provider = c.req.param('provider'); - logger.debug('Getting provider mappings by provider', { provider }); - - try { - const mappings = await exchangeService.getProviderMappingsByProvider(provider); - logger.info('Successfully retrieved provider mappings', { provider, count: mappings.length }); - - return c.json(createSuccessResponse(mappings, undefined, mappings.length)); - } catch (error) { - logger.error('Failed to get provider mappings', { error, provider }); - return handleError(c, error, 'to get provider mappings'); - } -}); - -// Update provider mapping (activate/deactivate, verify, change confidence) -exchangeRoutes.patch('/provider-mappings/:id', async c => { - const mappingId = c.req.param('id'); - logger.debug('Updating provider mapping', { mappingId }); - - try { - const body = await c.req.json(); - logger.debug('Received provider mapping update request', { mappingId, updates: body }); - - const validatedUpdates = validateUpdateProviderMapping(body); - logger.debug('Provider mapping update data validated', { mappingId, validatedUpdates }); - - const mapping = await exchangeService.updateProviderMapping(mappingId, validatedUpdates); - - if (!mapping) { - logger.warn('Provider mapping not found for update', { mappingId }); - return c.json(createSuccessResponse(null, 'Provider mapping not found'), 404); + try { + const mappings = await exchangeService.getAllProviderMappings(); + logger.info('Successfully retrieved all provider mappings', { count: mappings.length }); + return c.json(createSuccessResponse(mappings, undefined, mappings.length)); + } catch (error) { + logger.error('Failed to get provider mappings', { error }); + return handleError(c, error, 'to get provider mappings'); } + }); - logger.info('Provider mapping updated successfully', { - mappingId, - provider: mapping.provider, - providerExchangeCode: mapping.provider_exchange_code, - updates: validatedUpdates, - }); + // Get provider mappings by provider + exchangeRoutes.get('/provider-mappings/:provider', async c => { + const provider = c.req.param('provider'); + logger.debug('Getting provider mappings by provider', { provider }); - return c.json(createSuccessResponse(mapping, 'Provider mapping updated successfully')); - } catch (error) { - logger.error('Failed to update provider mapping', { error, mappingId }); - return handleError(c, error, 'to update provider mapping'); - } -}); + try { + const mappings = await exchangeService.getProviderMappingsByProvider(provider); + logger.info('Successfully retrieved provider mappings', { provider, count: mappings.length }); -// Create new provider mapping -exchangeRoutes.post('/provider-mappings', async c => { - logger.debug('Creating new provider mapping'); + return c.json(createSuccessResponse(mappings, undefined, mappings.length)); + } catch (error) { + logger.error('Failed to get provider mappings', { error, provider }); + return handleError(c, error, 'to get provider mappings'); + } + }); - try { - const body = await c.req.json(); - logger.debug('Received provider mapping creation request', { requestBody: body }); + // Update provider mapping (activate/deactivate, verify, change confidence) + exchangeRoutes.patch('/provider-mappings/:id', async c => { + const mappingId = c.req.param('id'); + logger.debug('Updating provider mapping', { mappingId }); - const validatedData = validateCreateProviderMapping(body); - logger.debug('Provider mapping data validated successfully', { validatedData }); + try { + const body = await c.req.json(); + logger.debug('Received provider mapping update request', { mappingId, updates: body }); - const mapping = await exchangeService.createProviderMapping(validatedData); - logger.info('Provider mapping created successfully', { - mappingId: mapping.id, - provider: mapping.provider, - providerExchangeCode: mapping.provider_exchange_code, - masterExchangeId: mapping.master_exchange_id, - }); + const validatedUpdates = validateUpdateProviderMapping(body); + logger.debug('Provider mapping update data validated', { mappingId, validatedUpdates }); - return c.json(createSuccessResponse(mapping, 'Provider mapping created successfully'), 201); - } catch (error) { - logger.error('Failed to create provider mapping', { error }); - return handleError(c, error, 'to create provider mapping'); - } -}); + const mapping = await exchangeService.updateProviderMapping(mappingId, validatedUpdates); -// Get all available providers -exchangeRoutes.get('/providers/list', async c => { - logger.debug('Getting providers list'); + if (!mapping) { + logger.warn('Provider mapping not found for update', { mappingId }); + return c.json(createSuccessResponse(null, 'Provider mapping not found'), 404); + } - try { - const providers = await exchangeService.getProviders(); - logger.info('Successfully retrieved providers list', { count: providers.length, providers }); - return c.json(createSuccessResponse(providers)); - } catch (error) { - logger.error('Failed to get providers list', { error }); - return handleError(c, error, 'to get providers list'); - } -}); + logger.info('Provider mapping updated successfully', { + mappingId, + provider: mapping.provider, + providerExchangeCode: mapping.provider_exchange_code, + updates: validatedUpdates, + }); -// Get unmapped provider exchanges by provider -exchangeRoutes.get('/provider-exchanges/unmapped/:provider', async c => { - const provider = c.req.param('provider'); - logger.debug('Getting unmapped provider exchanges', { provider }); + return c.json(createSuccessResponse(mapping, 'Provider mapping updated successfully')); + } catch (error) { + logger.error('Failed to update provider mapping', { error, mappingId }); + return handleError(c, error, 'to update provider mapping'); + } + }); - try { - const exchanges = await exchangeService.getUnmappedProviderExchanges(provider); - logger.info('Successfully retrieved unmapped provider exchanges', { - provider, - count: exchanges.length, - }); + // Create new provider mapping + exchangeRoutes.post('/provider-mappings', async c => { + logger.debug('Creating new provider mapping'); - return c.json(createSuccessResponse(exchanges, undefined, exchanges.length)); - } catch (error) { - logger.error('Failed to get unmapped provider exchanges', { error, provider }); - return handleError(c, error, 'to get unmapped provider exchanges'); - } -}); + try { + const body = await c.req.json(); + logger.debug('Received provider mapping creation request', { requestBody: body }); -// Get exchange statistics -exchangeRoutes.get('/stats/summary', async c => { - logger.debug('Getting exchange statistics'); + const validatedData = validateCreateProviderMapping(body); + logger.debug('Provider mapping data validated successfully', { validatedData }); - try { - const stats = await exchangeService.getExchangeStats(); - logger.info('Successfully retrieved exchange statistics', { stats }); - return c.json(createSuccessResponse(stats)); - } catch (error) { - logger.error('Failed to get exchange statistics', { error }); - return handleError(c, error, 'to get exchange statistics'); - } -}); + const mapping = await exchangeService.createProviderMapping(validatedData); + logger.info('Provider mapping created successfully', { + mappingId: mapping.id, + provider: mapping.provider, + providerExchangeCode: mapping.provider_exchange_code, + masterExchangeId: mapping.master_exchange_id, + }); + + return c.json(createSuccessResponse(mapping, 'Provider mapping created successfully'), 201); + } catch (error) { + logger.error('Failed to create provider mapping', { error }); + return handleError(c, error, 'to create provider mapping'); + } + }); + + // Get all available providers + exchangeRoutes.get('/providers/list', async c => { + logger.debug('Getting providers list'); + + try { + const providers = await exchangeService.getProviders(); + logger.info('Successfully retrieved providers list', { count: providers.length, providers }); + return c.json(createSuccessResponse(providers)); + } catch (error) { + logger.error('Failed to get providers list', { error }); + return handleError(c, error, 'to get providers list'); + } + }); + + // Get unmapped provider exchanges by provider + exchangeRoutes.get('/provider-exchanges/unmapped/:provider', async c => { + const provider = c.req.param('provider'); + logger.debug('Getting unmapped provider exchanges', { provider }); + + try { + const exchanges = await exchangeService.getUnmappedProviderExchanges(provider); + logger.info('Successfully retrieved unmapped provider exchanges', { + provider, + count: exchanges.length, + }); + + return c.json(createSuccessResponse(exchanges, undefined, exchanges.length)); + } catch (error) { + logger.error('Failed to get unmapped provider exchanges', { error, provider }); + return handleError(c, error, 'to get unmapped provider exchanges'); + } + }); + + // Get exchange statistics + exchangeRoutes.get('/stats/summary', async c => { + logger.debug('Getting exchange statistics'); + + try { + const stats = await exchangeService.getExchangeStats(); + logger.info('Successfully retrieved exchange statistics', { stats }); + return c.json(createSuccessResponse(stats)); + } catch (error) { + logger.error('Failed to get exchange statistics', { error }); + return handleError(c, error, 'to get exchange statistics'); + } + }); + + return exchangeRoutes; +} \ No newline at end of file diff --git a/apps/web-api/src/routes/index.ts b/apps/web-api/src/routes/index.ts index 8a1e802..61bb46e 100644 --- a/apps/web-api/src/routes/index.ts +++ b/apps/web-api/src/routes/index.ts @@ -1,5 +1,5 @@ /** * Routes index - exports all route modules */ -export { exchangeRoutes } from './exchange.routes'; +export { createExchangeRoutes } from './exchange.routes'; export { healthRoutes } from './health.routes'; diff --git a/apps/web-api/src/services/exchange.service.ts b/apps/web-api/src/services/exchange.service.ts index 95eafad..cb48694 100644 --- a/apps/web-api/src/services/exchange.service.ts +++ b/apps/web-api/src/services/exchange.service.ts @@ -1,5 +1,5 @@ import { getLogger } from '@stock-bot/logger'; -import { getMongoDBClient, getPostgreSQLClient } from '../clients'; +import type { IServiceContainer } from '@stock-bot/handlers'; import { CreateExchangeRequest, CreateProviderMappingRequest, @@ -15,12 +15,14 @@ import { const logger = getLogger('exchange-service'); export class ExchangeService { + constructor(private container: IServiceContainer) {} + private get postgresClient() { - return getPostgreSQLClient(); + return this.container.postgres; } private get mongoClient() { - return getMongoDBClient(); + return this.container.mongodb; } // Exchanges @@ -375,5 +377,7 @@ export class ExchangeService { } } -// Export singleton instance -export const exchangeService = new ExchangeService(); +// Export function to create service instance with container +export function createExchangeService(container: IServiceContainer): ExchangeService { + return new ExchangeService(container); +} \ No newline at end of file