fixed some issues in config service

This commit is contained in:
Bojan Kucera 2025-06-03 12:14:38 -04:00
parent 2f5309d80f
commit 23f7614b29
10 changed files with 125 additions and 43 deletions

View file

@ -61,7 +61,7 @@ The library automatically loads environment variables from `.env` files. You can
### Database Configuration ### Database Configuration
- `databaseConfig` - Database connection settings (Dragonfly, TimescaleDB) - `databaseConfig` - Database connection settings (Dragonfly, QuestDB, MongoDB, PostgreSQL)
### Data Provider Configuration ### Data Provider Configuration

View file

@ -4,7 +4,6 @@
import { describe, expect, test, beforeAll, afterAll } from 'bun:test'; import { describe, expect, test, beforeAll, afterAll } from 'bun:test';
import { import {
getEnvironment, getEnvironment,
Environment,
validateConfig, validateConfig,
ConfigurationError, ConfigurationError,
loadEnvVariables, loadEnvVariables,
@ -13,7 +12,7 @@ import {
getBooleanEnvVar getBooleanEnvVar
} from './core'; } from './core';
import { databaseConfigSchema } from './types'; import { Environment, databaseConfigSchema } from './types';
describe('Core configuration', () => { describe('Core configuration', () => {
// Save original environment variables // Save original environment variables
@ -74,7 +73,6 @@ describe('Core configuration', () => {
expect(getBooleanEnvVar('TEST_BOOL_FALSE')).toBe(false); expect(getBooleanEnvVar('TEST_BOOL_FALSE')).toBe(false);
expect(getBooleanEnvVar('NON_EXISTENT', true)).toBe(true); expect(getBooleanEnvVar('NON_EXISTENT', true)).toBe(true);
}); });
test('validateConfig validates against schema', () => { test('validateConfig validates against schema', () => {
// Valid config // Valid config
const validConfig = { const validConfig = {
@ -83,16 +81,28 @@ describe('Core configuration', () => {
port: 6379, port: 6379,
maxRetriesPerRequest: 3 maxRetriesPerRequest: 3
}, },
timescaleDB: { questDB: {
host: 'localhost',
port: 8812,
database: 'stockbot',
user: 'admin',
httpPort: 9000
},
mongodb: {
uri: 'mongodb://localhost:27017',
database: 'stockbot'
},
postgres: {
host: 'localhost', host: 'localhost',
port: 5432, port: 5432,
database: 'stockbot', database: 'stockbot',
user: 'postgres' user: 'postgres',
poolSize: 10,
ssl: false
} }
}; };
expect(() => validateConfig(validConfig, databaseConfigSchema)).not.toThrow(); expect(() => validateConfig(validConfig, databaseConfigSchema)).not.toThrow();
// Invalid config (missing required field) // Invalid config (missing required field)
const invalidConfig = { const invalidConfig = {
dragonfly: { dragonfly: {
@ -100,11 +110,24 @@ describe('Core configuration', () => {
// missing port // missing port
maxRetriesPerRequest: 3 maxRetriesPerRequest: 3
}, },
timescaleDB: { questDB: {
host: 'localhost',
port: 8812,
database: 'stockbot',
user: 'admin',
httpPort: 9000
},
mongodb: {
uri: 'mongodb://localhost:27017',
database: 'stockbot'
},
postgres: {
host: 'localhost', host: 'localhost',
port: 5432, port: 5432,
database: 'stockbot', database: 'stockbot',
user: 'postgres' user: 'postgres',
poolSize: 10,
ssl: false
} }
}; };

View file

@ -2,7 +2,7 @@
* Core configuration module for the Stock Bot platform * Core configuration module for the Stock Bot platform
*/ */
import { config as dotenvConfig } from 'dotenv'; import { config as dotenvConfig } from 'dotenv';
import path from 'path'; import path from 'node:path';
import { z } from 'zod'; import { z } from 'zod';
import { Environment } from './types'; import { Environment } from './types';

View file

@ -14,11 +14,24 @@ const defaultDatabaseConfig: DatabaseConfig = {
port: 6379, port: 6379,
maxRetriesPerRequest: 3 maxRetriesPerRequest: 3
}, },
timescaleDB: { questDB: {
host: 'localhost',
port: 8812,
database: 'stockbot',
user: 'admin',
httpPort: 9000
},
mongodb: {
uri: 'mongodb://localhost:27017',
database: 'stockbot'
},
postgres: {
host: 'localhost', host: 'localhost',
port: 5432, port: 5432,
database: 'stockbot', database: 'stockbot',
user: 'postgres' user: 'postgres',
poolSize: 10,
ssl: false
} }
}; };
@ -26,7 +39,7 @@ const defaultDatabaseConfig: DatabaseConfig = {
* Load database configuration from environment variables * Load database configuration from environment variables
*/ */
export function loadDatabaseConfig(): DatabaseConfig { export function loadDatabaseConfig(): DatabaseConfig {
const config: DatabaseConfig = { const config = {
dragonfly: { dragonfly: {
host: getEnvVar('DRAGONFLY_HOST') || defaultDatabaseConfig.dragonfly.host, host: getEnvVar('DRAGONFLY_HOST') || defaultDatabaseConfig.dragonfly.host,
port: getNumericEnvVar('DRAGONFLY_PORT', defaultDatabaseConfig.dragonfly.port), port: getNumericEnvVar('DRAGONFLY_PORT', defaultDatabaseConfig.dragonfly.port),
@ -34,12 +47,29 @@ export function loadDatabaseConfig(): DatabaseConfig {
maxRetriesPerRequest: getNumericEnvVar('DRAGONFLY_MAX_RETRIES_PER_REQUEST', maxRetriesPerRequest: getNumericEnvVar('DRAGONFLY_MAX_RETRIES_PER_REQUEST',
defaultDatabaseConfig.dragonfly.maxRetriesPerRequest) defaultDatabaseConfig.dragonfly.maxRetriesPerRequest)
}, },
timescaleDB: { questDB: {
host: getEnvVar('TIMESCALE_HOST') || defaultDatabaseConfig.timescaleDB.host, host: getEnvVar('QUESTDB_HOST') || defaultDatabaseConfig.questDB.host,
port: getNumericEnvVar('TIMESCALE_PORT', defaultDatabaseConfig.timescaleDB.port), port: getNumericEnvVar('QUESTDB_PORT', defaultDatabaseConfig.questDB.port),
database: getEnvVar('TIMESCALE_DB') || defaultDatabaseConfig.timescaleDB.database, database: getEnvVar('QUESTDB_DB') || defaultDatabaseConfig.questDB.database,
user: getEnvVar('TIMESCALE_USER') || defaultDatabaseConfig.timescaleDB.user, user: getEnvVar('QUESTDB_USER') || defaultDatabaseConfig.questDB.user,
password: getEnvVar('TIMESCALE_PASSWORD') password: getEnvVar('QUESTDB_PASSWORD'),
httpPort: getNumericEnvVar('QUESTDB_HTTP_PORT', defaultDatabaseConfig.questDB.httpPort)
},
mongodb: {
uri: getEnvVar('MONGODB_URI') || defaultDatabaseConfig.mongodb.uri,
database: getEnvVar('MONGODB_DATABASE') || defaultDatabaseConfig.mongodb.database,
username: getEnvVar('MONGODB_USERNAME'),
password: getEnvVar('MONGODB_PASSWORD'),
options: process.env.MONGODB_OPTIONS ? JSON.parse(process.env.MONGODB_OPTIONS) : undefined
},
postgres: {
host: getEnvVar('POSTGRES_HOST') || defaultDatabaseConfig.postgres.host,
port: getNumericEnvVar('POSTGRES_PORT', defaultDatabaseConfig.postgres.port),
database: getEnvVar('POSTGRES_DB') || defaultDatabaseConfig.postgres.database,
user: getEnvVar('POSTGRES_USER') || defaultDatabaseConfig.postgres.user,
password: getEnvVar('POSTGRES_PASSWORD'),
ssl: process.env.POSTGRES_SSL === 'true',
poolSize: getNumericEnvVar('POSTGRES_POOL_SIZE', defaultDatabaseConfig.postgres.poolSize)
} }
}; };

View file

@ -20,17 +20,24 @@ export function printCurrentConfig(): void {
console.log('\n=== Stock Bot Configuration ==='); console.log('\n=== Stock Bot Configuration ===');
console.log('\nEnvironment:', getEnvironment()); console.log('\nEnvironment:', getEnvironment());
console.log('\n--- Database Config ---'); console.log('\n--- Database Config ---');
console.log('Dragonfly Host:', databaseConfig.dragonfly.host); console.log('Dragonfly Host:', databaseConfig.dragonfly.host);
console.log('Dragonfly Port:', databaseConfig.dragonfly.port); console.log('Dragonfly Port:', databaseConfig.dragonfly.port);
console.log('TimescaleDB Host:', databaseConfig.timescaleDB.host); console.log('QuestDB Host:', databaseConfig.questDB.host);
console.log('TimescaleDB Database:', databaseConfig.timescaleDB.database); console.log('QuestDB Database:', databaseConfig.questDB.database);
console.log('MongoDB URI:', databaseConfig.mongodb.uri);
console.log('MongoDB Database:', databaseConfig.mongodb.database);
console.log('PostgreSQL Host:', databaseConfig.postgres.host);
console.log('PostgreSQL Database:', databaseConfig.postgres.database);
console.log('\n--- Data Provider Config ---'); console.log('\n--- Data Provider Config ---');
console.log('Default Provider:', dataProviderConfigs.defaultProvider); console.log('Default Provider:', dataProviderConfigs.defaultProvider);
console.log('Providers:'); console.log('Providers:');
dataProviderConfigs.providers.forEach(provider => { dataProviderConfigs.providers.forEach((provider: {
name: string;
type: string;
baseUrl?: string;
wsUrl?: string;
}) => {
console.log(` - ${provider.name} (${provider.type})`); console.log(` - ${provider.name} (${provider.type})`);
if (provider.baseUrl) console.log(` URL: ${provider.baseUrl}`); if (provider.baseUrl) console.log(` URL: ${provider.baseUrl}`);
if (provider.wsUrl) console.log(` WebSocket: ${provider.wsUrl}`); if (provider.wsUrl) console.log(` WebSocket: ${provider.wsUrl}`);
@ -59,14 +66,15 @@ export function printCurrentConfig(): void {
} }
// Execute example if this file is run directly // Execute example if this file is run directly
if (require.main === module) { if (require.main === module) { try {
try {
printCurrentConfig(); printCurrentConfig();
} catch (error) { } catch (error: unknown) {
if (error instanceof ConfigurationError) { if (error instanceof ConfigurationError) {
console.error('Configuration Error:', error.message); console.error('Configuration Error:', error.message);
} else if (error instanceof Error) {
console.error('Error:', error.message);
} else { } else {
console.error('Error:', error); console.error('Unknown error:', error);
} }
process.exit(1); process.exit(1);
} }

View file

@ -21,4 +21,4 @@ export * from './risk';
export * from './logging'; export * from './logging';
// Service-specific configurations // Service-specific configurations
export * from './services'; export * from './services/index';

View file

@ -2,9 +2,9 @@
* Market Data Gateway service configuration * Market Data Gateway service configuration
*/ */
import { z } from 'zod'; import { z } from 'zod';
import { getEnvVar, getNumericEnvVar, getBooleanEnvVar, createConfigLoader } from './core'; import { getEnvVar, getNumericEnvVar, getBooleanEnvVar, createConfigLoader } from '../core';
import { Environment, BaseConfig } from './types'; import { Environment, BaseConfig } from '../types';
import { getEnvironment } from './core'; import { getEnvironment } from '../core';
/** /**
* Market Data Gateway specific configuration schema * Market Data Gateway specific configuration schema

View file

@ -36,12 +36,29 @@ export const databaseConfigSchema = z.object({
password: z.string().optional(), password: z.string().optional(),
maxRetriesPerRequest: z.number().default(3) maxRetriesPerRequest: z.number().default(3)
}), }),
timescaleDB: z.object({ questDB: z.object({
host: z.string().default('localhost'),
port: z.number().default(8812),
database: z.string().default('stockbot'),
user: z.string().default('admin'),
password: z.string().optional(),
httpPort: z.number().default(9000)
}),
mongodb: z.object({
uri: z.string().default('mongodb://localhost:27017'),
database: z.string().default('stockbot'),
username: z.string().optional(),
password: z.string().optional(),
options: z.record(z.string(), z.any()).optional()
}),
postgres: z.object({
host: z.string().default('localhost'), host: z.string().default('localhost'),
port: z.number().default(5432), port: z.number().default(5432),
database: z.string().default('stockbot'), database: z.string().default('stockbot'),
user: z.string().default('postgres'), user: z.string().default('postgres'),
password: z.string().optional() password: z.string().optional(),
ssl: z.boolean().default(false),
poolSize: z.number().default(10)
}) })
}); });

View file

@ -3,7 +3,10 @@
"compilerOptions": { "compilerOptions": {
"outDir": "./dist", "outDir": "./dist",
"rootDir": "./src", "rootDir": "./src",
"declaration": true "declaration": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": false,
"module": "ESNext",
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"] "exclude": ["node_modules", "dist", "**/*.test.ts"]

View file

@ -5,8 +5,9 @@
"target": "ES2022", "target": "ES2022",
// Module configuration for different project types // Module configuration for different project types
"module": "NodeNext", "module": "ESNext",
"moduleResolution": "NodeNext", "moduleResolution": "bundler",
"composite": true,
// Type checking // Type checking
"strict": true, "strict": true,