nestjs-reverse-engineering
Version:
A powerful TypeScript/NestJS library for database reverse engineering, entity generation, and CRUD operations
350 lines • 15.6 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReverseEngineeringService = void 0;
/* eslint-disable prettier/prettier */
const common_1 = require("@nestjs/common");
const typeorm_1 = require("typeorm");
const introspector_factory_1 = require("./schema/introspector-factory");
const entity_builder_1 = require("./builders/entity-builder");
const crud_generator_new_1 = require("./builders/crud-generator-new");
const sql_generator_1 = require("./builders/sql-generator");
const data_exporter_1 = require("./builders/data-exporter");
const entity_index_generator_1 = require("./builders/entity-index-generator");
const default_config_1 = require("./config/default.config");
const path = require("path");
let ReverseEngineeringService = class ReverseEngineeringService {
constructor(dataSource) {
this.dataSource = dataSource;
}
/**
* Initialize the service with configuration
*/
initialize(configInput) {
this.config = (0, default_config_1.createConfig)(configInput);
console.log('🔧 Reverse Engineering Service initialized with configuration');
}
/**
* Get configuration (create default if not initialized)
*/
getConfig() {
if (!this.config) {
throw new Error('Service not initialized. Call initialize() with configuration first.');
}
return this.config;
}
/**
* Create a database connection from configuration
*/
async createDataSource(dbConfig) {
const { DataSource } = await Promise.resolve().then(() => require('typeorm'));
// Ensure password is a string
const sanitizedConfig = {
...dbConfig,
password: String(dbConfig.password || ''),
port: Number(dbConfig.port) || 5432
};
console.log(`🔌 Connecting to ${sanitizedConfig.type}://${sanitizedConfig.username}@${sanitizedConfig.host}:${sanitizedConfig.port}/${sanitizedConfig.database}`);
return new DataSource({
type: sanitizedConfig.type,
host: sanitizedConfig.host,
port: sanitizedConfig.port,
username: sanitizedConfig.username,
password: sanitizedConfig.password,
database: sanitizedConfig.database,
schema: sanitizedConfig.schema,
ssl: sanitizedConfig.ssl,
entities: [],
synchronize: false,
});
}
/**
* Generate TypeScript entities from database schema
*/
async generateEntities(configOverride) {
const config = configOverride ? (0, default_config_1.createConfig)({ ...this.getConfig(), ...configOverride }) : this.getConfig();
if (!config.features.entities) {
console.log('⏭️ Entity generation disabled in configuration');
return;
}
console.log('🔄 Starting entity generation process...');
console.log(`📁 Output path: ${config.paths.entities}`);
// Create or use existing data source
const dataSource = this.dataSource || await this.createDataSource(config.database);
if (!dataSource.isInitialized) {
await dataSource.initialize();
}
try {
// Get database schema
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(dataSource);
const schema = await introspector.getDatabaseSchema();
console.log(`🗄️ Found ${schema.tables.length} tables in ${schema.dialect} database`);
// Filter tables based on configuration
let filteredTables = schema.tables;
if (config.entities.includedTables && config.entities.includedTables.length > 0) {
filteredTables = filteredTables.filter(table => config.entities.includedTables.includes(table.tableName));
}
if (config.entities.excludedTables && config.entities.excludedTables.length > 0) {
filteredTables = filteredTables.filter(table => !config.entities.excludedTables.includes(table.tableName));
}
console.log(`📊 Processing ${filteredTables.length} tables after filtering`);
// Generate entities
const entityOptions = {
outputPath: config.paths.entities,
generateInterfaces: false,
generateRepositories: false,
useDataTransferObjects: false,
includeComments: true,
namingConvention: 'camelCase',
includeRelations: config.entities.useTypeORM,
};
const entityBuilder = new entity_builder_1.EntityBuilder(entityOptions, filteredTables, schema.dialect);
for (const table of filteredTables) {
try {
await entityBuilder.generateEntity(table);
console.log(` ✅ Generated entity for ${table.tableName}`);
}
catch (error) {
console.error(` ❌ Failed to generate entity for table ${table.tableName}:`, error);
}
}
// Generate index file if enabled
if (config.features.generateIndex) {
const indexGenerator = new entity_index_generator_1.EntityIndexGenerator({
entitiesPath: config.paths.entities,
outputPath: path.join(config.paths.entities, 'index.ts')
});
await indexGenerator.generateIndex();
console.log(` 📄 Generated index file`);
}
console.log('✅ Entity generation process completed!');
}
finally {
// Only destroy if we created the connection
if (!this.dataSource && dataSource.isInitialized) {
await dataSource.destroy();
}
}
}
/**
* Generate CRUD operations for tables
*/
async generateCrud(configOverride) {
const config = configOverride ? (0, default_config_1.createConfig)({ ...this.getConfig(), ...configOverride }) : this.getConfig();
if (!config.features.crud) {
console.log('⏭️ CRUD generation disabled in configuration');
return;
}
console.log('🔄 Starting CRUD generation process...');
// Create or use existing data source
const dataSource = this.dataSource || await this.createDataSource(config.database);
if (!dataSource.isInitialized) {
await dataSource.initialize();
}
try {
// Get database schema
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(dataSource);
const schema = await introspector.getDatabaseSchema();
console.log(`🗄️ Found ${schema.tables.length} tables in ${schema.dialect} database`);
// Generate CRUD operations
const crudGenerator = new crud_generator_new_1.CrudGenerator(config);
const result = await crudGenerator.generateCrudForTables(schema.tables);
console.log(`✅ CRUD generation completed!`);
console.log(`📊 ${result.tablesProcessed} tables processed`);
console.log(`📁 ${result.filesGenerated} files generated`);
console.log(`📋 App module: ${result.appModulePath}`);
}
finally {
// Only destroy if we created the connection
if (!this.dataSource && dataSource.isInitialized) {
await dataSource.destroy();
}
}
}
/**
* Generate SQL scripts
*/
async generateSql(configOverride) {
const config = configOverride ? (0, default_config_1.createConfig)({ ...this.getConfig(), ...configOverride }) : this.getConfig();
if (!config.features.sql) {
console.log('⏭️ SQL generation disabled in configuration');
return;
}
console.log('🔄 Starting SQL generation process...');
console.log(`📁 Output path: ${config.paths.sql}`);
// Create or use existing data source
const dataSource = this.dataSource || await this.createDataSource(config.database);
if (!dataSource.isInitialized) {
await dataSource.initialize();
}
try {
// Get database schema
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(dataSource);
const schema = await introspector.getDatabaseSchema();
// Generate SQL scripts
const sqlGenerator = new sql_generator_1.SqlGenerator();
const result = await sqlGenerator.generateCreateTableScripts(schema.tables);
// Create output directory
const fs = await Promise.resolve().then(() => require('fs'));
const path = await Promise.resolve().then(() => require('path'));
if (!fs.existsSync(config.paths.sql)) {
fs.mkdirSync(config.paths.sql, { recursive: true });
}
// Write CREATE TABLE scripts
const createTablesFile = path.join(config.paths.sql, 'create-tables.sql');
fs.writeFileSync(createTablesFile, result.sql);
console.log(`📄 Created: ${createTablesFile}`);
console.log(`✅ SQL generation completed!`);
console.log(`📁 Generated ${result.tableCount} table scripts in ${config.paths.sql}`);
}
finally {
if (!this.dataSource && dataSource.isInitialized) {
await dataSource.destroy();
}
}
}
/**
* Export data with optional masking
*/
async exportData(configOverride) {
const config = configOverride ? (0, default_config_1.createConfig)({ ...this.getConfig(), ...configOverride }) : this.getConfig();
if (!config.features.dataExport) {
console.log('⏭️ Data export disabled in configuration');
return;
}
console.log('🔄 Starting data export process...');
// Create or use existing data source
const dataSource = this.dataSource || await this.createDataSource(config.database);
if (!dataSource.isInitialized) {
await dataSource.initialize();
}
try {
// Get database schema
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(dataSource);
const schema = await introspector.getDatabaseSchema();
// Export data
const dataExporter = new data_exporter_1.DataExporter(dataSource, {
dialect: config.database.type,
outputPath: config.paths.dataExport,
batchSize: config.dataExport.batchSize,
dataMasking: {
enabled: config.dataExport.enableMasking,
sensitiveFields: config.dataExport.maskedFields,
maskingPatterns: {}
}
});
const tableNames = schema.tables.map(t => t.tableName);
const result = await dataExporter.exportTables(tableNames);
console.log(`✅ Data export completed!`);
console.log(`📊 ${result.tableCount} tables processed`);
console.log(`📁 ${result.fileCount} files generated`);
}
finally {
if (!this.dataSource && dataSource.isInitialized) {
await dataSource.destroy();
}
}
}
/**
* Complete reverse engineering process (entities + CRUD + SQL + data export)
*/
async generateAll(configOverride) {
const config = configOverride ? (0, default_config_1.createConfig)({ ...this.getConfig(), ...configOverride }) : this.getConfig();
console.log('🚀 Starting complete reverse engineering process...');
if (config.features.entities) {
await this.generateEntities(configOverride);
}
if (config.features.crud) {
await this.generateCrud(configOverride);
}
if (config.features.sql) {
await this.generateSql(configOverride);
}
if (config.features.dataExport) {
await this.exportData(configOverride);
}
console.log('🎉 Complete reverse engineering process finished!');
}
/**
* Test database connection
*/
async testConnection(dbConfig) {
const config = dbConfig || this.getConfig().database;
console.log('🔌 Testing database connection...');
try {
const dataSource = await this.createDataSource(config);
await dataSource.initialize();
console.log('✅ Database connection successful!');
await dataSource.destroy();
return true;
}
catch (error) {
console.error('❌ Database connection failed:', error.message);
return false;
}
}
/**
* Get database schema information
*/
async getSchema(dbConfig) {
const config = dbConfig || this.getConfig().database;
const dataSource = await this.createDataSource(config);
await dataSource.initialize();
try {
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(dataSource);
return await introspector.getDatabaseSchema();
}
finally {
await dataSource.destroy();
}
}
/**
* Get list of tables
*/
async getTables(dbConfig) {
const schema = await this.getSchema(dbConfig);
return schema.tables;
}
/**
* Validate configuration
*/
validateConfig(configInput) {
try {
const config = (0, default_config_1.createConfig)(configInput);
// Validate database configuration
if (!config.database.host || !config.database.database) {
throw new Error('Database host and database name are required');
}
// Validate paths
if (!config.paths.baseOutput) {
throw new Error('Base output path is required');
}
console.log('✅ Configuration is valid');
return true;
}
catch (error) {
console.error('❌ Configuration validation failed:', error.message);
return false;
}
}
/**
* Get current configuration
*/
getConfiguration() {
return this.config || null;
}
};
exports.ReverseEngineeringService = ReverseEngineeringService;
exports.ReverseEngineeringService = ReverseEngineeringService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [typeorm_1.DataSource])
], ReverseEngineeringService);
//# sourceMappingURL=reverse-engineering-service-new.js.map