getting aligned and refactored
This commit is contained in:
parent
60ada5f6a3
commit
a3459f5865
16 changed files with 296 additions and 273 deletions
Binary file not shown.
|
|
@ -3,7 +3,7 @@
|
||||||
* Configures dependency injection for the data pipeline service
|
* 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 { getLogger } from '@stock-bot/logger';
|
||||||
import type { AppConfig } from '@stock-bot/config';
|
import type { AppConfig } from '@stock-bot/config';
|
||||||
|
|
||||||
|
|
@ -14,8 +14,8 @@ const logger = getLogger('data-pipeline-container');
|
||||||
*/
|
*/
|
||||||
export function setupServiceContainer(
|
export function setupServiceContainer(
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): ServiceContainer {
|
): IServiceContainer {
|
||||||
logger.info('Configuring data pipeline service container...');
|
logger.info('Configuring data pipeline service container...');
|
||||||
|
|
||||||
// Data pipeline specific configuration
|
// Data pipeline specific configuration
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import { handlerRegistry, createJobHandler, type HandlerConfig, type ScheduledJobConfig } from '@stock-bot/queue';
|
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';
|
import { exchangeOperations } from './operations';
|
||||||
|
|
||||||
const logger = getLogger('exchanges-handler');
|
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...');
|
logger.info('Registering exchanges handler...');
|
||||||
|
|
||||||
// Update operations to use container
|
// Update operations to use container
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
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';
|
import type { JobPayload } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('enhanced-sync-clear-postgresql-data');
|
const logger = getLogger('enhanced-sync-clear-postgresql-data');
|
||||||
|
|
||||||
export async function clearPostgreSQLData(
|
export async function clearPostgreSQLData(
|
||||||
payload: JobPayload,
|
payload: JobPayload,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<{
|
): Promise<{
|
||||||
exchangesCleared: number;
|
exchangesCleared: number;
|
||||||
symbolsCleared: number;
|
symbolsCleared: number;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
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';
|
import type { JobPayload, SyncStatus } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('enhanced-sync-status');
|
const logger = getLogger('enhanced-sync-status');
|
||||||
|
|
||||||
export async function getSyncStatus(
|
export async function getSyncStatus(
|
||||||
payload: JobPayload,
|
payload: JobPayload,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<SyncStatus[]> {
|
): Promise<SyncStatus[]> {
|
||||||
logger.info('Getting comprehensive sync status...');
|
logger.info('Getting comprehensive sync status...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
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';
|
import type { JobPayload } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('enhanced-sync-exchange-stats');
|
const logger = getLogger('enhanced-sync-exchange-stats');
|
||||||
|
|
||||||
export async function getExchangeStats(
|
export async function getExchangeStats(
|
||||||
payload: JobPayload,
|
payload: JobPayload,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
logger.info('Getting exchange statistics...');
|
logger.info('Getting exchange statistics...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
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';
|
import type { JobPayload } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('enhanced-sync-provider-mapping-stats');
|
const logger = getLogger('enhanced-sync-provider-mapping-stats');
|
||||||
|
|
||||||
export async function getProviderMappingStats(
|
export async function getProviderMappingStats(
|
||||||
payload: JobPayload,
|
payload: JobPayload,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
logger.info('Getting provider mapping statistics...');
|
logger.info('Getting provider mapping statistics...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import { getMongoDBClient, getPostgreSQLClient } from '../../../clients';
|
import { getMongoDBClient, getPostgreSQLClient } from '../../../clients';
|
||||||
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import type { JobPayload } from '../../../types/job-payloads';
|
import type { JobPayload } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('sync-qm-exchanges');
|
const logger = getLogger('sync-qm-exchanges');
|
||||||
|
|
||||||
export async function syncQMExchanges(
|
export async function syncQMExchanges(
|
||||||
payload: JobPayload
|
payload: JobPayload,
|
||||||
|
container: IServiceContainer
|
||||||
): Promise<{ processed: number; created: number; updated: number }> {
|
): Promise<{ processed: number; created: number; updated: number }> {
|
||||||
logger.info('Starting QM exchanges sync...');
|
logger.info('Starting QM exchanges sync...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mongoClient = getMongoDBClient();
|
const mongoClient = container.mongodb;
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
|
|
||||||
// 1. Get all QM exchanges from MongoDB
|
// 1. Get all QM exchanges from MongoDB
|
||||||
const qmExchanges = await mongoClient.find('qmExchanges', {});
|
const qmExchanges = await mongoClient.find('qmExchanges', {});
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
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';
|
import type { JobPayload, SyncResult } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('enhanced-sync-all-exchanges');
|
const logger = getLogger('enhanced-sync-all-exchanges');
|
||||||
|
|
||||||
export async function syncAllExchanges(payload: JobPayload, container: ServiceContainer): Promise<SyncResult> {
|
export async function syncAllExchanges(payload: JobPayload, container: IServiceContainer): Promise<SyncResult> {
|
||||||
const clearFirst = payload.clearFirst || true;
|
const clearFirst = payload.clearFirst || true;
|
||||||
logger.info('Starting comprehensive exchange sync...', { clearFirst });
|
logger.info('Starting comprehensive exchange sync...', { clearFirst });
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ async function clearPostgreSQLData(postgresClient: any): Promise<void> {
|
||||||
logger.info('PostgreSQL data cleared successfully');
|
logger.info('PostgreSQL data cleared successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncEODExchanges(container: ServiceContainer): Promise<SyncResult> {
|
async function syncEODExchanges(container: IServiceContainer): Promise<SyncResult> {
|
||||||
const mongoClient = container.mongodb;
|
const mongoClient = container.mongodb;
|
||||||
const exchanges = await mongoClient.find('eodExchanges', { active: true });
|
const exchanges = await mongoClient.find('eodExchanges', { active: true });
|
||||||
const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 };
|
const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 };
|
||||||
|
|
@ -96,7 +96,7 @@ async function syncEODExchanges(container: ServiceContainer): Promise<SyncResult
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncIBExchanges(container: ServiceContainer): Promise<SyncResult> {
|
async function syncIBExchanges(container: IServiceContainer): Promise<SyncResult> {
|
||||||
const mongoClient = container.mongodb;
|
const mongoClient = container.mongodb;
|
||||||
const exchanges = await mongoClient.find('ibExchanges', {});
|
const exchanges = await mongoClient.find('ibExchanges', {});
|
||||||
const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 };
|
const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 };
|
||||||
|
|
@ -132,7 +132,7 @@ async function createProviderExchangeMapping(
|
||||||
countryCode: string | null,
|
countryCode: string | null,
|
||||||
currency: string | null,
|
currency: string | null,
|
||||||
confidence: number,
|
confidence: number,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!providerExchangeCode) {
|
if (!providerExchangeCode) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -181,7 +181,7 @@ async function findOrCreateMasterExchange(
|
||||||
providerName: string,
|
providerName: string,
|
||||||
countryCode: string | null,
|
countryCode: string | null,
|
||||||
currency: string | null,
|
currency: string | null,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const postgresClient = container.postgres;
|
const postgresClient = container.postgres;
|
||||||
|
|
||||||
|
|
@ -237,7 +237,7 @@ function getBasicExchangeMapping(providerCode: string): string | null {
|
||||||
async function findProviderExchangeMapping(
|
async function findProviderExchangeMapping(
|
||||||
provider: string,
|
provider: string,
|
||||||
providerExchangeCode: string,
|
providerExchangeCode: string,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const postgresClient = container.postgres;
|
const postgresClient = container.postgres;
|
||||||
const query =
|
const query =
|
||||||
|
|
@ -246,7 +246,7 @@ async function findProviderExchangeMapping(
|
||||||
return result.rows[0] || null;
|
return result.rows[0] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findExchangeByCode(code: string, container: ServiceContainer): Promise<any> {
|
async function findExchangeByCode(code: string, container: IServiceContainer): Promise<any> {
|
||||||
const postgresClient = container.postgres;
|
const postgresClient = container.postgres;
|
||||||
const query = 'SELECT * FROM exchanges WHERE code = $1';
|
const query = 'SELECT * FROM exchanges WHERE code = $1';
|
||||||
const result = await postgresClient.query(query, [code]);
|
const result = await postgresClient.query(query, [code]);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import type { MasterExchange } from '@stock-bot/mongodb';
|
import type { MasterExchange } from '@stock-bot/mongodb';
|
||||||
import { getMongoDBClient } from '../../../clients';
|
import { getMongoDBClient } from '../../../clients';
|
||||||
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import type { JobPayload } from '../../../types/job-payloads';
|
import type { JobPayload } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('sync-ib-exchanges');
|
const logger = getLogger('sync-ib-exchanges');
|
||||||
|
|
@ -15,12 +16,13 @@ interface IBExchange {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function syncIBExchanges(
|
export async function syncIBExchanges(
|
||||||
payload: JobPayload
|
payload: JobPayload,
|
||||||
|
container: IServiceContainer
|
||||||
): Promise<{ syncedCount: number; totalExchanges: number }> {
|
): Promise<{ syncedCount: number; totalExchanges: number }> {
|
||||||
logger.info('Syncing IB exchanges from database...');
|
logger.info('Syncing IB exchanges from database...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mongoClient = getMongoDBClient();
|
const mongoClient = container.mongodb;
|
||||||
const db = mongoClient.getDatabase();
|
const db = mongoClient.getDatabase();
|
||||||
|
|
||||||
// Filter by country code US and CA
|
// Filter by country code US and CA
|
||||||
|
|
@ -37,7 +39,7 @@ export async function syncIBExchanges(
|
||||||
|
|
||||||
for (const exchange of ibExchanges) {
|
for (const exchange of ibExchanges) {
|
||||||
try {
|
try {
|
||||||
await createOrUpdateMasterExchange(exchange);
|
await createOrUpdateMasterExchange(exchange, container);
|
||||||
syncedCount++;
|
syncedCount++;
|
||||||
|
|
||||||
logger.debug('Synced IB exchange', {
|
logger.debug('Synced IB exchange', {
|
||||||
|
|
@ -64,8 +66,8 @@ export async function syncIBExchanges(
|
||||||
/**
|
/**
|
||||||
* Create or update master exchange record 1:1 from IB exchange
|
* Create or update master exchange record 1:1 from IB exchange
|
||||||
*/
|
*/
|
||||||
async function createOrUpdateMasterExchange(ibExchange: IBExchange): Promise<void> {
|
async function createOrUpdateMasterExchange(ibExchange: IBExchange, container: IServiceContainer): Promise<void> {
|
||||||
const mongoClient = getMongoDBClient();
|
const mongoClient = container.mongodb;
|
||||||
const db = mongoClient.getDatabase();
|
const db = mongoClient.getDatabase();
|
||||||
const collection = db.collection<MasterExchange>('masterExchanges');
|
const collection = db.collection<MasterExchange>('masterExchanges');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import { getMongoDBClient, getPostgreSQLClient } from '../../../clients';
|
import { getMongoDBClient, getPostgreSQLClient } from '../../../clients';
|
||||||
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import type { JobPayload, SyncResult } from '../../../types/job-payloads';
|
import type { JobPayload, SyncResult } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('enhanced-sync-qm-provider-mappings');
|
const logger = getLogger('enhanced-sync-qm-provider-mappings');
|
||||||
|
|
||||||
export async function syncQMProviderMappings(payload: JobPayload): Promise<SyncResult> {
|
export async function syncQMProviderMappings(
|
||||||
|
payload: JobPayload,
|
||||||
|
container: IServiceContainer
|
||||||
|
): Promise<SyncResult> {
|
||||||
logger.info('Starting QM provider exchange mappings sync...');
|
logger.info('Starting QM provider exchange mappings sync...');
|
||||||
|
|
||||||
const result: SyncResult = {
|
const result: SyncResult = {
|
||||||
|
|
@ -16,8 +20,8 @@ export async function syncQMProviderMappings(payload: JobPayload): Promise<SyncR
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mongoClient = getMongoDBClient();
|
const mongoClient = container.mongodb;
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
|
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await postgresClient.query('BEGIN');
|
await postgresClient.query('BEGIN');
|
||||||
|
|
@ -59,7 +63,8 @@ export async function syncQMProviderMappings(payload: JobPayload): Promise<SyncR
|
||||||
exchange.sampleExchange || exchange.exchangeCode,
|
exchange.sampleExchange || exchange.exchangeCode,
|
||||||
exchange.countryCode,
|
exchange.countryCode,
|
||||||
exchange.countryCode === 'CA' ? 'CAD' : 'USD', // Simple currency mapping
|
exchange.countryCode === 'CA' ? 'CAD' : 'USD', // Simple currency mapping
|
||||||
0.8 // good confidence for QM data
|
0.8, // good confidence for QM data
|
||||||
|
container
|
||||||
);
|
);
|
||||||
|
|
||||||
result.processed++;
|
result.processed++;
|
||||||
|
|
@ -75,29 +80,31 @@ export async function syncQMProviderMappings(payload: JobPayload): Promise<SyncR
|
||||||
logger.info('QM provider exchange mappings sync completed', result);
|
logger.info('QM provider exchange mappings sync completed', result);
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
await postgresClient.query('ROLLBACK');
|
await postgresClient.query('ROLLBACK');
|
||||||
logger.error('QM provider exchange mappings sync failed', { error });
|
logger.error('QM provider exchange mappings sync failed', { error });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function createProviderExchangeMapping(
|
async function createProviderExchangeMapping(
|
||||||
provider: string,
|
provider: string,
|
||||||
providerExchangeCode: string,
|
providerExchangeCode: string,
|
||||||
providerExchangeName: string,
|
providerExchangeName: string,
|
||||||
countryCode: string | null,
|
countryCode: string | null,
|
||||||
currency: string | null,
|
currency: string | null,
|
||||||
confidence: number
|
confidence: number,
|
||||||
|
container: IServiceContainer
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!providerExchangeCode) {
|
if (!providerExchangeCode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
|
|
||||||
// Check if mapping already exists
|
// Check if mapping already exists
|
||||||
const existingMapping = await findProviderExchangeMapping(provider, providerExchangeCode);
|
const existingMapping = await findProviderExchangeMapping(provider, providerExchangeCode, container);
|
||||||
if (existingMapping) {
|
if (existingMapping) {
|
||||||
// Don't override existing mappings to preserve manual work
|
// Don't override existing mappings to preserve manual work
|
||||||
return;
|
return;
|
||||||
|
|
@ -108,7 +115,8 @@ async function createProviderExchangeMapping(
|
||||||
providerExchangeCode,
|
providerExchangeCode,
|
||||||
providerExchangeName,
|
providerExchangeName,
|
||||||
countryCode,
|
countryCode,
|
||||||
currency
|
currency,
|
||||||
|
container
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create the provider exchange mapping
|
// Create the provider exchange mapping
|
||||||
|
|
@ -133,9 +141,10 @@ async function createProviderExchangeMapping(
|
||||||
|
|
||||||
async function findProviderExchangeMapping(
|
async function findProviderExchangeMapping(
|
||||||
provider: string,
|
provider: string,
|
||||||
providerExchangeCode: string
|
providerExchangeCode: string,
|
||||||
|
container: IServiceContainer
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
const query =
|
const query =
|
||||||
'SELECT * FROM provider_exchange_mappings WHERE provider = $1 AND provider_exchange_code = $2';
|
'SELECT * FROM provider_exchange_mappings WHERE provider = $1 AND provider_exchange_code = $2';
|
||||||
const result = await postgresClient.query(query, [provider, providerExchangeCode]);
|
const result = await postgresClient.query(query, [provider, providerExchangeCode]);
|
||||||
|
|
@ -146,12 +155,13 @@ async function findOrCreateMasterExchange(
|
||||||
providerCode: string,
|
providerCode: string,
|
||||||
providerName: string,
|
providerName: string,
|
||||||
countryCode: string | null,
|
countryCode: string | null,
|
||||||
currency: string | null
|
currency: string | null,
|
||||||
|
container: IServiceContainer
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
|
|
||||||
// First, try to find exact match
|
// First, try to find exact match
|
||||||
let masterExchange = await findExchangeByCode(providerCode);
|
let masterExchange = await findExchangeByCode(providerCode, container);
|
||||||
|
|
||||||
if (masterExchange) {
|
if (masterExchange) {
|
||||||
return masterExchange;
|
return masterExchange;
|
||||||
|
|
@ -160,7 +170,7 @@ async function findOrCreateMasterExchange(
|
||||||
// Try to find by similar codes (basic mapping)
|
// Try to find by similar codes (basic mapping)
|
||||||
const basicMapping = getBasicExchangeMapping(providerCode);
|
const basicMapping = getBasicExchangeMapping(providerCode);
|
||||||
if (basicMapping) {
|
if (basicMapping) {
|
||||||
masterExchange = await findExchangeByCode(basicMapping);
|
masterExchange = await findExchangeByCode(basicMapping, container);
|
||||||
if (masterExchange) {
|
if (masterExchange) {
|
||||||
return masterExchange;
|
return masterExchange;
|
||||||
}
|
}
|
||||||
|
|
@ -199,8 +209,8 @@ function getBasicExchangeMapping(providerCode: string): string | null {
|
||||||
return mappings[providerCode.toUpperCase()] || null;
|
return mappings[providerCode.toUpperCase()] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findExchangeByCode(code: string): Promise<any> {
|
async function findExchangeByCode(code: string, container: IServiceContainer): Promise<any> {
|
||||||
const postgresClient = getPostgreSQLClient();
|
const postgresClient = container.postgres;
|
||||||
const query = 'SELECT * FROM exchanges WHERE code = $1';
|
const query = 'SELECT * FROM exchanges WHERE code = $1';
|
||||||
const result = await postgresClient.query(query, [code]);
|
const result = await postgresClient.query(query, [code]);
|
||||||
return result.rows[0] || null;
|
return result.rows[0] || null;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Configures dependency injection for the web API service
|
* 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 { getLogger } from '@stock-bot/logger';
|
||||||
import type { AppConfig } from '@stock-bot/config';
|
import type { AppConfig } from '@stock-bot/config';
|
||||||
|
|
||||||
|
|
@ -14,8 +14,8 @@ const logger = getLogger('web-api-container');
|
||||||
*/
|
*/
|
||||||
export function setupServiceContainer(
|
export function setupServiceContainer(
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
container: ServiceContainer
|
container: IServiceContainer
|
||||||
): ServiceContainer {
|
): IServiceContainer {
|
||||||
logger.info('Configuring web API service container...');
|
logger.info('Configuring web API service container...');
|
||||||
|
|
||||||
// Web API specific configuration
|
// Web API specific configuration
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import type { ServiceContainer } from '@stock-bot/di';
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import { healthRoutes, exchangeRoutes } from './index';
|
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();
|
const app = new Hono();
|
||||||
|
|
||||||
// Add container to context for all routes
|
// Create routes with container
|
||||||
app.use('*', async (c, next) => {
|
const exchangeRoutes = createExchangeRoutes(container);
|
||||||
c.set('container', container);
|
|
||||||
await next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mount routes
|
// Mount routes
|
||||||
app.route('/health', healthRoutes);
|
app.route('/health', healthRoutes);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
*/
|
*/
|
||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import { getLogger } from '@stock-bot/logger';
|
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 { createSuccessResponse, handleError } from '../utils/error-handler';
|
||||||
import {
|
import {
|
||||||
validateCreateExchange,
|
validateCreateExchange,
|
||||||
|
|
@ -13,243 +14,249 @@ import {
|
||||||
} from '../utils/validation';
|
} from '../utils/validation';
|
||||||
|
|
||||||
const logger = getLogger('exchange-routes');
|
const logger = getLogger('exchange-routes');
|
||||||
export const exchangeRoutes = new Hono();
|
|
||||||
|
|
||||||
// Get all exchanges with provider mapping counts and mappings
|
export function createExchangeRoutes(container: IServiceContainer) {
|
||||||
exchangeRoutes.get('/', async c => {
|
const exchangeRoutes = new Hono();
|
||||||
logger.debug('Getting all exchanges');
|
const exchangeService = createExchangeService(container);
|
||||||
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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get exchange by ID with detailed provider mappings
|
// Get all exchanges with provider mapping counts and mappings
|
||||||
exchangeRoutes.get('/:id', async c => {
|
exchangeRoutes.get('/', async c => {
|
||||||
const exchangeId = c.req.param('id');
|
logger.debug('Getting all exchanges');
|
||||||
logger.debug('Getting exchange by ID', { exchangeId });
|
try {
|
||||||
|
const exchanges = await exchangeService.getAllExchanges();
|
||||||
try {
|
logger.info('Successfully retrieved exchanges', { count: exchanges.length });
|
||||||
const result = await exchangeService.getExchangeById(exchangeId);
|
return c.json(createSuccessResponse(exchanges, undefined, exchanges.length));
|
||||||
|
} catch (error) {
|
||||||
if (!result) {
|
logger.error('Failed to get exchanges', { error });
|
||||||
logger.warn('Exchange not found', { exchangeId });
|
return handleError(c, error, 'to get exchanges');
|
||||||
return c.json(createSuccessResponse(null, 'Exchange not found'), 404);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
logger.info('Successfully retrieved exchange details', {
|
// Get exchange by ID with detailed provider mappings
|
||||||
exchangeId,
|
exchangeRoutes.get('/:id', async c => {
|
||||||
exchangeCode: result.exchange.code,
|
const exchangeId = c.req.param('id');
|
||||||
mappingCount: result.provider_mappings.length,
|
logger.debug('Getting exchange by ID', { exchangeId });
|
||||||
});
|
|
||||||
return c.json(createSuccessResponse(result));
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to get exchange details', { error, exchangeId });
|
|
||||||
return handleError(c, error, 'to get exchange details');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create new exchange
|
try {
|
||||||
exchangeRoutes.post('/', async c => {
|
const result = await exchangeService.getExchangeById(exchangeId);
|
||||||
logger.debug('Creating new exchange');
|
|
||||||
|
|
||||||
try {
|
if (!result) {
|
||||||
const body = await c.req.json();
|
logger.warn('Exchange not found', { exchangeId });
|
||||||
logger.debug('Received exchange creation request', { requestBody: body });
|
return c.json(createSuccessResponse(null, 'Exchange not found'), 404);
|
||||||
|
}
|
||||||
|
|
||||||
const validatedData = validateCreateExchange(body);
|
logger.info('Successfully retrieved exchange details', {
|
||||||
logger.debug('Exchange data validated successfully', { validatedData });
|
exchangeId,
|
||||||
|
exchangeCode: result.exchange.code,
|
||||||
const exchange = await exchangeService.createExchange(validatedData);
|
mappingCount: result.provider_mappings.length,
|
||||||
logger.info('Exchange created successfully', {
|
});
|
||||||
exchangeId: exchange.id,
|
return c.json(createSuccessResponse(result));
|
||||||
code: exchange.code,
|
} catch (error) {
|
||||||
name: exchange.name,
|
logger.error('Failed to get exchange details', { error, exchangeId });
|
||||||
});
|
return handleError(c, error, 'to get exchange details');
|
||||||
|
|
||||||
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', {
|
// Create new exchange
|
||||||
exchangeId,
|
exchangeRoutes.post('/', async c => {
|
||||||
code: exchange.code,
|
logger.debug('Creating new exchange');
|
||||||
updates: validatedUpdates,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Log special actions
|
try {
|
||||||
if (validatedUpdates.visible === false) {
|
const body = await c.req.json();
|
||||||
logger.warn('Exchange marked as hidden - provider mappings will be deleted', {
|
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,
|
exchangeId,
|
||||||
code: exchange.code,
|
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'));
|
// Get all provider mappings
|
||||||
} catch (error) {
|
exchangeRoutes.get('/provider-mappings/all', async c => {
|
||||||
logger.error('Failed to update exchange', { error, exchangeId });
|
logger.debug('Getting all provider mappings');
|
||||||
return handleError(c, error, 'to update exchange');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get all provider mappings
|
try {
|
||||||
exchangeRoutes.get('/provider-mappings/all', async c => {
|
const mappings = await exchangeService.getAllProviderMappings();
|
||||||
logger.debug('Getting all provider mappings');
|
logger.info('Successfully retrieved all provider mappings', { count: mappings.length });
|
||||||
|
return c.json(createSuccessResponse(mappings, undefined, mappings.length));
|
||||||
try {
|
} catch (error) {
|
||||||
const mappings = await exchangeService.getAllProviderMappings();
|
logger.error('Failed to get provider mappings', { error });
|
||||||
logger.info('Successfully retrieved all provider mappings', { count: mappings.length });
|
return handleError(c, error, 'to get provider mappings');
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
logger.info('Provider mapping updated successfully', {
|
// Get provider mappings by provider
|
||||||
mappingId,
|
exchangeRoutes.get('/provider-mappings/:provider', async c => {
|
||||||
provider: mapping.provider,
|
const provider = c.req.param('provider');
|
||||||
providerExchangeCode: mapping.provider_exchange_code,
|
logger.debug('Getting provider mappings by provider', { provider });
|
||||||
updates: validatedUpdates,
|
|
||||||
});
|
|
||||||
|
|
||||||
return c.json(createSuccessResponse(mapping, 'Provider mapping updated successfully'));
|
try {
|
||||||
} catch (error) {
|
const mappings = await exchangeService.getProviderMappingsByProvider(provider);
|
||||||
logger.error('Failed to update provider mapping', { error, mappingId });
|
logger.info('Successfully retrieved provider mappings', { provider, count: mappings.length });
|
||||||
return handleError(c, error, 'to update provider mapping');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create new provider mapping
|
return c.json(createSuccessResponse(mappings, undefined, mappings.length));
|
||||||
exchangeRoutes.post('/provider-mappings', async c => {
|
} catch (error) {
|
||||||
logger.debug('Creating new provider mapping');
|
logger.error('Failed to get provider mappings', { error, provider });
|
||||||
|
return handleError(c, error, 'to get provider mappings');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
// Update provider mapping (activate/deactivate, verify, change confidence)
|
||||||
const body = await c.req.json();
|
exchangeRoutes.patch('/provider-mappings/:id', async c => {
|
||||||
logger.debug('Received provider mapping creation request', { requestBody: body });
|
const mappingId = c.req.param('id');
|
||||||
|
logger.debug('Updating provider mapping', { mappingId });
|
||||||
|
|
||||||
const validatedData = validateCreateProviderMapping(body);
|
try {
|
||||||
logger.debug('Provider mapping data validated successfully', { validatedData });
|
const body = await c.req.json();
|
||||||
|
logger.debug('Received provider mapping update request', { mappingId, updates: body });
|
||||||
|
|
||||||
const mapping = await exchangeService.createProviderMapping(validatedData);
|
const validatedUpdates = validateUpdateProviderMapping(body);
|
||||||
logger.info('Provider mapping created successfully', {
|
logger.debug('Provider mapping update data validated', { mappingId, validatedUpdates });
|
||||||
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);
|
const mapping = await exchangeService.updateProviderMapping(mappingId, validatedUpdates);
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to create provider mapping', { error });
|
|
||||||
return handleError(c, error, 'to create provider mapping');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get all available providers
|
if (!mapping) {
|
||||||
exchangeRoutes.get('/providers/list', async c => {
|
logger.warn('Provider mapping not found for update', { mappingId });
|
||||||
logger.debug('Getting providers list');
|
return c.json(createSuccessResponse(null, 'Provider mapping not found'), 404);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
logger.info('Provider mapping updated successfully', {
|
||||||
const providers = await exchangeService.getProviders();
|
mappingId,
|
||||||
logger.info('Successfully retrieved providers list', { count: providers.length, providers });
|
provider: mapping.provider,
|
||||||
return c.json(createSuccessResponse(providers));
|
providerExchangeCode: mapping.provider_exchange_code,
|
||||||
} catch (error) {
|
updates: validatedUpdates,
|
||||||
logger.error('Failed to get providers list', { error });
|
});
|
||||||
return handleError(c, error, 'to get providers list');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get unmapped provider exchanges by provider
|
return c.json(createSuccessResponse(mapping, 'Provider mapping updated successfully'));
|
||||||
exchangeRoutes.get('/provider-exchanges/unmapped/:provider', async c => {
|
} catch (error) {
|
||||||
const provider = c.req.param('provider');
|
logger.error('Failed to update provider mapping', { error, mappingId });
|
||||||
logger.debug('Getting unmapped provider exchanges', { provider });
|
return handleError(c, error, 'to update provider mapping');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
// Create new provider mapping
|
||||||
const exchanges = await exchangeService.getUnmappedProviderExchanges(provider);
|
exchangeRoutes.post('/provider-mappings', async c => {
|
||||||
logger.info('Successfully retrieved unmapped provider exchanges', {
|
logger.debug('Creating new provider mapping');
|
||||||
provider,
|
|
||||||
count: exchanges.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
return c.json(createSuccessResponse(exchanges, undefined, exchanges.length));
|
try {
|
||||||
} catch (error) {
|
const body = await c.req.json();
|
||||||
logger.error('Failed to get unmapped provider exchanges', { error, provider });
|
logger.debug('Received provider mapping creation request', { requestBody: body });
|
||||||
return handleError(c, error, 'to get unmapped provider exchanges');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get exchange statistics
|
const validatedData = validateCreateProviderMapping(body);
|
||||||
exchangeRoutes.get('/stats/summary', async c => {
|
logger.debug('Provider mapping data validated successfully', { validatedData });
|
||||||
logger.debug('Getting exchange statistics');
|
|
||||||
|
|
||||||
try {
|
const mapping = await exchangeService.createProviderMapping(validatedData);
|
||||||
const stats = await exchangeService.getExchangeStats();
|
logger.info('Provider mapping created successfully', {
|
||||||
logger.info('Successfully retrieved exchange statistics', { stats });
|
mappingId: mapping.id,
|
||||||
return c.json(createSuccessResponse(stats));
|
provider: mapping.provider,
|
||||||
} catch (error) {
|
providerExchangeCode: mapping.provider_exchange_code,
|
||||||
logger.error('Failed to get exchange statistics', { error });
|
masterExchangeId: mapping.master_exchange_id,
|
||||||
return handleError(c, error, 'to get exchange statistics');
|
});
|
||||||
}
|
|
||||||
});
|
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;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Routes index - exports all route modules
|
* Routes index - exports all route modules
|
||||||
*/
|
*/
|
||||||
export { exchangeRoutes } from './exchange.routes';
|
export { createExchangeRoutes } from './exchange.routes';
|
||||||
export { healthRoutes } from './health.routes';
|
export { healthRoutes } from './health.routes';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import { getMongoDBClient, getPostgreSQLClient } from '../clients';
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import {
|
import {
|
||||||
CreateExchangeRequest,
|
CreateExchangeRequest,
|
||||||
CreateProviderMappingRequest,
|
CreateProviderMappingRequest,
|
||||||
|
|
@ -15,12 +15,14 @@ import {
|
||||||
const logger = getLogger('exchange-service');
|
const logger = getLogger('exchange-service');
|
||||||
|
|
||||||
export class ExchangeService {
|
export class ExchangeService {
|
||||||
|
constructor(private container: IServiceContainer) {}
|
||||||
|
|
||||||
private get postgresClient() {
|
private get postgresClient() {
|
||||||
return getPostgreSQLClient();
|
return this.container.postgres;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get mongoClient() {
|
private get mongoClient() {
|
||||||
return getMongoDBClient();
|
return this.container.mongodb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exchanges
|
// Exchanges
|
||||||
|
|
@ -375,5 +377,7 @@ export class ExchangeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export singleton instance
|
// Export function to create service instance with container
|
||||||
export const exchangeService = new ExchangeService();
|
export function createExchangeService(container: IServiceContainer): ExchangeService {
|
||||||
|
return new ExchangeService(container);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue