feat: implement database service with SQL Server and MySQL support
- Add database types (QueryResult, TransactionResult) - Implement DatabaseService with connection management for SQL Server and MySQL - Add query execution methods for both database types - Include comprehensive error handling and logging - Add unit test for error handling - Configure Jest for testing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
8
jest.config.js
Normal file
8
jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/tests'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
|
||||||
|
moduleFileExtensions: ['ts', 'js', 'json'],
|
||||||
|
};
|
||||||
9
src/main/models/database.types.ts
Normal file
9
src/main/models/database.types.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface QueryResult {
|
||||||
|
rows: any[];
|
||||||
|
rowCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionResult {
|
||||||
|
success: boolean;
|
||||||
|
rowsAffected: number;
|
||||||
|
}
|
||||||
109
src/main/services/database.service.ts
Normal file
109
src/main/services/database.service.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import sql from 'mssql';
|
||||||
|
import mysql from 'mysql2/promise';
|
||||||
|
import { SQLServerConfig, MySQLConfig } from '../models/config.types';
|
||||||
|
import { QueryResult } from '../models/database.types';
|
||||||
|
import { LoggerService } from './logger.service';
|
||||||
|
|
||||||
|
export class DatabaseService {
|
||||||
|
private sqlServerPool?: sql.ConnectionPool;
|
||||||
|
private mysqlPool?: mysql.Pool;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private sqlServerConfig: SQLServerConfig,
|
||||||
|
private mysqlConfig: MySQLConfig
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async connectToSQLServer(): Promise<void> {
|
||||||
|
try {
|
||||||
|
LoggerService.info('Connecting to SQL Server...');
|
||||||
|
|
||||||
|
this.sqlServerPool = await sql.connect({
|
||||||
|
server: this.sqlServerConfig.server,
|
||||||
|
database: this.sqlServerConfig.database,
|
||||||
|
user: this.sqlServerConfig.username,
|
||||||
|
password: this.sqlServerConfig.password,
|
||||||
|
driver: this.sqlServerConfig.driver,
|
||||||
|
options: {
|
||||||
|
trustServerCertificate:
|
||||||
|
this.sqlServerConfig.trustServerCertificate === 'yes',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
LoggerService.info('SQL Server connection established');
|
||||||
|
} catch (error) {
|
||||||
|
LoggerService.error('Failed to connect to SQL Server', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectToMySQL(): Promise<void> {
|
||||||
|
try {
|
||||||
|
LoggerService.info('Connecting to MySQL...');
|
||||||
|
|
||||||
|
this.mysqlPool = mysql.createPool({
|
||||||
|
host: this.mysqlConfig.host,
|
||||||
|
port: this.mysqlConfig.port,
|
||||||
|
database: this.mysqlConfig.database,
|
||||||
|
user: this.mysqlConfig.username,
|
||||||
|
password: this.mysqlConfig.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
LoggerService.info('MySQL connection established');
|
||||||
|
} catch (error) {
|
||||||
|
LoggerService.error('Failed to connect to MySQL', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeSQLServerQuery(query: string, params?: any[]): Promise<QueryResult> {
|
||||||
|
if (!this.sqlServerPool) {
|
||||||
|
throw new Error('SQL Server not connected');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const request = this.sqlServerPool.request();
|
||||||
|
if (params) {
|
||||||
|
params.forEach((param, index) => {
|
||||||
|
request.input(`param${index}`, param);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await request.query(query);
|
||||||
|
return {
|
||||||
|
rows: result.recordset,
|
||||||
|
rowCount: result.rowsAffected[0],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
LoggerService.error('SQL Server query failed', { query, error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeMySQLQuery(query: string, params?: any[]): Promise<QueryResult> {
|
||||||
|
if (!this.mysqlPool) {
|
||||||
|
throw new Error('MySQL not connected');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [rows] = await this.mysqlPool.execute(query, params);
|
||||||
|
return {
|
||||||
|
rows: rows as any[],
|
||||||
|
rowCount: (rows as any[]).length,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
LoggerService.error('MySQL query failed', { query, error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeAll(): Promise<void> {
|
||||||
|
if (this.sqlServerPool) {
|
||||||
|
await this.sqlServerPool.close();
|
||||||
|
LoggerService.info('SQL Server connection closed');
|
||||||
|
}
|
||||||
|
if (this.mysqlPool) {
|
||||||
|
await this.mysqlPool.end();
|
||||||
|
LoggerService.info('MySQL connection closed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
tests/unit/services/database.service.test.ts
Normal file
26
tests/unit/services/database.service.test.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { DatabaseService } from '../../../src/main/services/database.service';
|
||||||
|
|
||||||
|
describe('DatabaseService', () => {
|
||||||
|
it('should throw error when querying without connection', async () => {
|
||||||
|
const service = new DatabaseService(
|
||||||
|
{
|
||||||
|
server: 'localhost',
|
||||||
|
database: 'test',
|
||||||
|
username: 'test',
|
||||||
|
password: 'test',
|
||||||
|
driver: 'test',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
host: 'localhost',
|
||||||
|
port: 3306,
|
||||||
|
database: 'test',
|
||||||
|
username: 'test',
|
||||||
|
password: 'test',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.executeSQLServerQuery('SELECT 1')
|
||||||
|
).rejects.toThrow('SQL Server not connected');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user