@hed-hog/cli 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/dist/scripts/deploy.d.ts +1 -0
- package/dist/scripts/deploy.js +84 -0
- package/dist/scripts/deploy.js.map +1 -0
- package/dist/src/app.module.d.ts +2 -0
- package/dist/src/app.module.js +58 -0
- package/dist/src/app.module.js.map +1 -0
- package/dist/src/commands/add.command.d.ts +14 -0
- package/dist/src/commands/add.command.js +58 -0
- package/dist/src/commands/add.command.js.map +1 -0
- package/dist/src/commands/dev.command/backupdb.subcommand.d.ts +10 -0
- package/dist/src/commands/dev.command/backupdb.subcommand.js +47 -0
- package/dist/src/commands/dev.command/backupdb.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/create-library.subcommand.d.ts +15 -0
- package/dist/src/commands/dev.command/create-library.subcommand.js +88 -0
- package/dist/src/commands/dev.command/create-library.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/hash-directory.subcommand.d.ts +12 -0
- package/dist/src/commands/dev.command/hash-directory.subcommand.js +70 -0
- package/dist/src/commands/dev.command/hash-directory.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/initdb.subcommand.d.ts +10 -0
- package/dist/src/commands/dev.command/initdb.subcommand.js +47 -0
- package/dist/src/commands/dev.command/initdb.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/reset.subcommand.d.ts +10 -0
- package/dist/src/commands/dev.command/reset.subcommand.js +47 -0
- package/dist/src/commands/dev.command/reset.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/restoredb.subcommand.d.ts +10 -0
- package/dist/src/commands/dev.command/restoredb.subcommand.js +47 -0
- package/dist/src/commands/dev.command/restoredb.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/route.subcommand.d.ts +10 -0
- package/dist/src/commands/dev.command/route.subcommand.js +47 -0
- package/dist/src/commands/dev.command/route.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command/tsconfig.subcommand.d.ts +10 -0
- package/dist/src/commands/dev.command/tsconfig.subcommand.js +47 -0
- package/dist/src/commands/dev.command/tsconfig.subcommand.js.map +1 -0
- package/dist/src/commands/dev.command.d.ts +4 -0
- package/dist/src/commands/dev.command.js +40 -0
- package/dist/src/commands/dev.command.js.map +1 -0
- package/dist/src/commands/new.command.d.ts +43 -0
- package/dist/src/commands/new.command.js +436 -0
- package/dist/src/commands/new.command.js.map +1 -0
- package/dist/src/functions/to-pascal-case.d.ts +1 -0
- package/dist/src/functions/to-pascal-case.js +10 -0
- package/dist/src/functions/to-pascal-case.js.map +1 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.js +10 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/modules/database/database.module.d.ts +2 -0
- package/dist/src/modules/database/database.module.js +22 -0
- package/dist/src/modules/database/database.module.js.map +1 -0
- package/dist/src/modules/database/database.service.d.ts +14 -0
- package/dist/src/modules/database/database.service.js +186 -0
- package/dist/src/modules/database/database.service.js.map +1 -0
- package/dist/src/modules/developer/developer.module.d.ts +2 -0
- package/dist/src/modules/developer/developer.module.js +22 -0
- package/dist/src/modules/developer/developer.module.js.map +1 -0
- package/dist/src/modules/developer/developer.service.d.ts +37 -0
- package/dist/src/modules/developer/developer.service.js +900 -0
- package/dist/src/modules/developer/developer.service.js.map +1 -0
- package/dist/src/modules/git/git.module.d.ts +2 -0
- package/dist/src/modules/git/git.module.js +23 -0
- package/dist/src/modules/git/git.module.js.map +1 -0
- package/dist/src/modules/git/git.service.d.ts +8 -0
- package/dist/src/modules/git/git.service.js +67 -0
- package/dist/src/modules/git/git.service.js.map +1 -0
- package/dist/src/modules/hedhog/hedhog.module.d.ts +2 -0
- package/dist/src/modules/hedhog/hedhog.module.js +41 -0
- package/dist/src/modules/hedhog/hedhog.module.js.map +1 -0
- package/dist/src/modules/hedhog/hedhog.service.d.ts +44 -0
- package/dist/src/modules/hedhog/hedhog.service.js +350 -0
- package/dist/src/modules/hedhog/hedhog.service.js.map +1 -0
- package/dist/src/modules/hedhog/services/file-system.service.d.ts +22 -0
- package/dist/src/modules/hedhog/services/file-system.service.js +230 -0
- package/dist/src/modules/hedhog/services/file-system.service.js.map +1 -0
- package/dist/src/modules/hedhog/services/migration.service.d.ts +39 -0
- package/dist/src/modules/hedhog/services/migration.service.js +767 -0
- package/dist/src/modules/hedhog/services/migration.service.js.map +1 -0
- package/dist/src/modules/hedhog/services/module.service.d.ts +10 -0
- package/dist/src/modules/hedhog/services/module.service.js +135 -0
- package/dist/src/modules/hedhog/services/module.service.js.map +1 -0
- package/dist/src/modules/hedhog/services/table.service.d.ts +49 -0
- package/dist/src/modules/hedhog/services/table.service.js +432 -0
- package/dist/src/modules/hedhog/services/table.service.js.map +1 -0
- package/dist/src/modules/hedhog/services/template.service.d.ts +13 -0
- package/dist/src/modules/hedhog/services/template.service.js +88 -0
- package/dist/src/modules/hedhog/services/template.service.js.map +1 -0
- package/dist/src/modules/package/package.module.d.ts +2 -0
- package/dist/src/modules/package/package.module.js +23 -0
- package/dist/src/modules/package/package.module.js.map +1 -0
- package/dist/src/modules/package/package.service.d.ts +9 -0
- package/dist/src/modules/package/package.service.js +94 -0
- package/dist/src/modules/package/package.service.js.map +1 -0
- package/dist/src/modules/runner/runner.module.d.ts +2 -0
- package/dist/src/modules/runner/runner.module.js +23 -0
- package/dist/src/modules/runner/runner.module.js.map +1 -0
- package/dist/src/modules/runner/runner.service.d.ts +14 -0
- package/dist/src/modules/runner/runner.service.js +69 -0
- package/dist/src/modules/runner/runner.service.js.map +1 -0
- package/dist/src/questions/database.question.d.ts +12 -0
- package/dist/src/questions/database.question.js +61 -0
- package/dist/src/questions/database.question.js.map +1 -0
- package/dist/src/questions/project-name.question.d.ts +3 -0
- package/dist/src/questions/project-name.question.js +34 -0
- package/dist/src/questions/project-name.question.js.map +1 -0
- package/dist/templates/database/touch_updated_at.sql.ejs +9 -0
- package/dist/templates/database/trg_touch_updated_at.sql.ejs +2 -0
- package/dist/templates/library/.eslintrc.js.ejs +9 -0
- package/dist/templates/library/.prettierrc.js.ejs +4 -0
- package/dist/templates/library/index.ts.ejs +1 -0
- package/dist/templates/library/init.app.module.ts.ejs +25 -0
- package/dist/templates/library/init.package.json.ejs +61 -0
- package/dist/templates/library/module.ts.ejs +15 -0
- package/dist/templates/library/package.json.ejs +36 -0
- package/dist/templates/library/tsconfig.json.ejs +11 -0
- package/dist/templates/library/tsconfig.production.json.ejs +46 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MigrationService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const chalk = require("chalk");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
const sql_formatter_1 = require("sql-formatter");
|
|
17
|
+
const database_service_1 = require("../../database/database.service");
|
|
18
|
+
const developer_service_1 = require("../../developer/developer.service");
|
|
19
|
+
const file_system_service_1 = require("./file-system.service");
|
|
20
|
+
const table_service_1 = require("./table.service");
|
|
21
|
+
let MigrationService = class MigrationService {
|
|
22
|
+
database;
|
|
23
|
+
developer;
|
|
24
|
+
fileSystem;
|
|
25
|
+
tableService;
|
|
26
|
+
indexWithClauseLocale = 0;
|
|
27
|
+
touchUpdatedAtCreated = false;
|
|
28
|
+
verbose = false;
|
|
29
|
+
constructor(database, developer, fileSystem, tableService) {
|
|
30
|
+
this.database = database;
|
|
31
|
+
this.developer = developer;
|
|
32
|
+
this.fileSystem = fileSystem;
|
|
33
|
+
this.tableService = tableService;
|
|
34
|
+
}
|
|
35
|
+
log(message) {
|
|
36
|
+
if (this.verbose) {
|
|
37
|
+
console.info('\n', message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getDefaultValue(column) {
|
|
41
|
+
if (column.type.toUpperCase() === 'TIMESTAMPTZ' &&
|
|
42
|
+
column.default === 'now()') {
|
|
43
|
+
return 'now()';
|
|
44
|
+
}
|
|
45
|
+
else if (column.name === 'order' && column.type.toUpperCase() === 'INT') {
|
|
46
|
+
return '0';
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return `'${column.default}'`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
getWhereString(where) {
|
|
53
|
+
if (!where || typeof where !== 'object' || !where.where)
|
|
54
|
+
return '';
|
|
55
|
+
const conditions = [];
|
|
56
|
+
for (const [key, value] of Object.entries(where.where)) {
|
|
57
|
+
if (typeof value === 'object' && value !== null) {
|
|
58
|
+
if ('in' in value && Array.isArray(value.in)) {
|
|
59
|
+
conditions.push(`"${key}" IN (${value.in.map((v) => `'${v}'`).join(', ')})`);
|
|
60
|
+
}
|
|
61
|
+
else if ('notIn' in value && Array.isArray(value.notIn)) {
|
|
62
|
+
conditions.push(`"${key}" NOT IN (${value.notIn.map((v) => `'${v}'`).join(', ')})`);
|
|
63
|
+
}
|
|
64
|
+
else if ('contains' in value) {
|
|
65
|
+
conditions.push(`"${key}" LIKE '%${value.contains}%'`);
|
|
66
|
+
}
|
|
67
|
+
else if ('notContains' in value) {
|
|
68
|
+
conditions.push(`"${key}" NOT LIKE '%${value.notContains}%'`);
|
|
69
|
+
}
|
|
70
|
+
else if ('not' in value) {
|
|
71
|
+
conditions.push(`"${key}" <> '${value.not}'`);
|
|
72
|
+
}
|
|
73
|
+
else if ('like' in value) {
|
|
74
|
+
conditions.push(`"${key}" LIKE '${value.like}'`);
|
|
75
|
+
}
|
|
76
|
+
else if ('notLike' in value) {
|
|
77
|
+
conditions.push(`"${key}" NOT LIKE '${value.notLike}'`);
|
|
78
|
+
}
|
|
79
|
+
else if ('gt' in value) {
|
|
80
|
+
conditions.push(`"${key}" > '${value.gt}'`);
|
|
81
|
+
}
|
|
82
|
+
else if ('gte' in value) {
|
|
83
|
+
conditions.push(`"${key}" >= '${value.gte}'`);
|
|
84
|
+
}
|
|
85
|
+
else if ('lt' in value) {
|
|
86
|
+
conditions.push(`"${key}" < '${value.lt}'`);
|
|
87
|
+
}
|
|
88
|
+
else if ('lte' in value) {
|
|
89
|
+
conditions.push(`"${key}" <= '${value.lte}'`);
|
|
90
|
+
}
|
|
91
|
+
else if ('equals' in value) {
|
|
92
|
+
conditions.push(`"${key}" = '${value.equals}'`);
|
|
93
|
+
}
|
|
94
|
+
else if ('notEquals' in value) {
|
|
95
|
+
conditions.push(`"${key}" <> '${value.notEquals}'`);
|
|
96
|
+
}
|
|
97
|
+
else if ('isNull' in value) {
|
|
98
|
+
conditions.push(`"${key}" IS NULL`);
|
|
99
|
+
}
|
|
100
|
+
else if ('isNotNull' in value) {
|
|
101
|
+
conditions.push(`"${key}" IS NOT NULL`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.warn(chalk.yellow(`Unknown condition for key "${key}": ${JSON.stringify(value)}`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
conditions.push(`"${key}" = '${value}'`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
112
|
+
}
|
|
113
|
+
processEnums(tables, dbType, migrationContent) {
|
|
114
|
+
for (const table of tables) {
|
|
115
|
+
this.log(`Processing enums for table: ${table.name}`);
|
|
116
|
+
for (const column of table.columns) {
|
|
117
|
+
if (dbType === 'postgres' &&
|
|
118
|
+
column.type === 'enum' &&
|
|
119
|
+
(column.enum || column.values)) {
|
|
120
|
+
this.log(`Creating enum for column: ${column.name}`);
|
|
121
|
+
const enumName = `${table.name}_${column.name}_enum`;
|
|
122
|
+
migrationContent.push(`-- CreateEnum`, `CREATE TYPE "${enumName}" AS ENUM (${((column.enum || column.values) ?? []).map((value) => `'${value}'`).join(', ')});`, ``);
|
|
123
|
+
column.type = enumName;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async createTableMigrations(tables, dbType, migrationContent, createdTables, cwd) {
|
|
129
|
+
if (!this.touchUpdatedAtCreated && tables.length > 0) {
|
|
130
|
+
// Verifica se já existe uma migration com a função touch_updated_at
|
|
131
|
+
let hasTouchUpdatedAt = false;
|
|
132
|
+
try {
|
|
133
|
+
const migrationsDir = (0, path_1.resolve)(cwd, './apps/api/prisma/migrations');
|
|
134
|
+
if (await this.fileSystem.exists(migrationsDir)) {
|
|
135
|
+
const migrationFolders = await this.fileSystem.listFiles(migrationsDir, (f) => true);
|
|
136
|
+
for (const folder of migrationFolders) {
|
|
137
|
+
const folderPath = (0, path_1.join)(migrationsDir, folder);
|
|
138
|
+
if (await this.fileSystem.exists(folderPath)) {
|
|
139
|
+
const files = await this.fileSystem.listFiles(folderPath, (f) => f.endsWith('.sql'));
|
|
140
|
+
for (const file of files) {
|
|
141
|
+
const filePath = (0, path_1.join)(folderPath, file);
|
|
142
|
+
const content = await this.fileSystem.readFileContent(filePath);
|
|
143
|
+
if (/CREATE\s+FUNCTION\s+touch_updated_at\b/i.test(content)) {
|
|
144
|
+
hasTouchUpdatedAt = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (hasTouchUpdatedAt)
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error(chalk.red(`Error checking for touch_updated_at function in migrations: ${error.message}`));
|
|
156
|
+
}
|
|
157
|
+
// Só adiciona se não existir ainda
|
|
158
|
+
if (!hasTouchUpdatedAt) {
|
|
159
|
+
try {
|
|
160
|
+
const templatePath = (0, path_1.resolve)(__dirname, '../../../templates/database/touch_updated_at.sql.ejs');
|
|
161
|
+
if (await this.fileSystem.exists(templatePath)) {
|
|
162
|
+
const templateContent = await this.fileSystem.readFileContent(templatePath);
|
|
163
|
+
migrationContent.push(`-- Function to auto-update updated_at timestamp`, templateContent);
|
|
164
|
+
this.touchUpdatedAtCreated = true;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.warn(chalk.yellow(`Template not found: ${templatePath}. Skipping touch_updated_at function.`));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error(chalk.red(`Error loading touch_updated_at.sql.ejs template: ${error.message}`));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const table of tables) {
|
|
176
|
+
this.log(`Creating migration for table: ${table.name}`);
|
|
177
|
+
const columns = table.columns;
|
|
178
|
+
if (dbType === 'postgres') {
|
|
179
|
+
createdTables.push(table.name);
|
|
180
|
+
migrationContent.push(`-- CreateTable`, `CREATE TABLE "${table.name}" (`);
|
|
181
|
+
const columnsContent = [];
|
|
182
|
+
for (const column of columns) {
|
|
183
|
+
this.log(`Adding column: ${column.name} (${column.type})`);
|
|
184
|
+
let columnDefinition = `"${column.name}" `;
|
|
185
|
+
switch (column.type.toLowerCase()) {
|
|
186
|
+
case 'pk':
|
|
187
|
+
columnDefinition += 'SERIAL';
|
|
188
|
+
break;
|
|
189
|
+
case 'fk':
|
|
190
|
+
columnDefinition += 'INTEGER';
|
|
191
|
+
break;
|
|
192
|
+
case 'int':
|
|
193
|
+
columnDefinition += 'INTEGER';
|
|
194
|
+
break;
|
|
195
|
+
case 'slug':
|
|
196
|
+
columnDefinition += `VARCHAR(${column.length || 255})`;
|
|
197
|
+
break;
|
|
198
|
+
case 'order':
|
|
199
|
+
columnDefinition += 'INTEGER';
|
|
200
|
+
break;
|
|
201
|
+
case 'enum':
|
|
202
|
+
columnDefinition += `"${column.type}"`;
|
|
203
|
+
break;
|
|
204
|
+
case 'datetime':
|
|
205
|
+
case 'created_at':
|
|
206
|
+
case 'updated_at':
|
|
207
|
+
columnDefinition += 'TIMESTAMPTZ';
|
|
208
|
+
break;
|
|
209
|
+
case 'locale_varchar':
|
|
210
|
+
columnDefinition += `VARCHAR(${column.length || 255})`;
|
|
211
|
+
break;
|
|
212
|
+
case 'locale_text':
|
|
213
|
+
columnDefinition += 'TEXT';
|
|
214
|
+
break;
|
|
215
|
+
case 'char':
|
|
216
|
+
columnDefinition += `CHAR(${column.length || 1})`;
|
|
217
|
+
break;
|
|
218
|
+
case 'bytes':
|
|
219
|
+
columnDefinition += 'BYTEA';
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
const validPostgresTypes = [
|
|
223
|
+
'smallint',
|
|
224
|
+
'integer',
|
|
225
|
+
'bigint',
|
|
226
|
+
'decimal',
|
|
227
|
+
'numeric',
|
|
228
|
+
'real',
|
|
229
|
+
'double precision',
|
|
230
|
+
'serial',
|
|
231
|
+
'bigserial',
|
|
232
|
+
'money',
|
|
233
|
+
'varchar',
|
|
234
|
+
'char',
|
|
235
|
+
'text',
|
|
236
|
+
'bytea',
|
|
237
|
+
'timestamp',
|
|
238
|
+
'timestamp(6)',
|
|
239
|
+
'timestamptz',
|
|
240
|
+
'timestamptz(6)',
|
|
241
|
+
'date',
|
|
242
|
+
'time',
|
|
243
|
+
'boolean',
|
|
244
|
+
'json',
|
|
245
|
+
'jsonb',
|
|
246
|
+
'uuid',
|
|
247
|
+
'inet',
|
|
248
|
+
'cidr',
|
|
249
|
+
'macaddr',
|
|
250
|
+
'point',
|
|
251
|
+
'line',
|
|
252
|
+
'lseg',
|
|
253
|
+
'box',
|
|
254
|
+
'path',
|
|
255
|
+
'polygon',
|
|
256
|
+
'circle',
|
|
257
|
+
'interval',
|
|
258
|
+
'tsvector',
|
|
259
|
+
'tsquery',
|
|
260
|
+
'xml',
|
|
261
|
+
'enum',
|
|
262
|
+
];
|
|
263
|
+
if (!validPostgresTypes.includes(column.type.toLowerCase()) &&
|
|
264
|
+
!column.type.endsWith('_enum')) {
|
|
265
|
+
console.error(chalk.red(`Invalid Postgres type for column "${column.name}": ${column.type}`));
|
|
266
|
+
throw new Error(`Invalid Postgres type for column "${column.name}": ${column.type}`);
|
|
267
|
+
}
|
|
268
|
+
columnDefinition += column.type.toUpperCase();
|
|
269
|
+
}
|
|
270
|
+
columnDefinition += column.isNullable ? ' NULL' : ' NOT NULL';
|
|
271
|
+
if (column.default !== undefined) {
|
|
272
|
+
columnDefinition += ` DEFAULT ${this.getDefaultValue(column)}`;
|
|
273
|
+
}
|
|
274
|
+
columnsContent.push(columnDefinition);
|
|
275
|
+
}
|
|
276
|
+
// Add primary key constraint(s)
|
|
277
|
+
for (const column of columns) {
|
|
278
|
+
if (column.isPrimaryKey || column.type === 'pk') {
|
|
279
|
+
this.log(`Adding primary key: ${column.name}`);
|
|
280
|
+
columnsContent.push(`CONSTRAINT "${table.name}_pkey" PRIMARY KEY ("${column.name}")`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
migrationContent.push(columnsContent.join(',\n'), ');', '');
|
|
284
|
+
if (table.columns.find((col) => col.name === 'updated_at' || col.type === 'updated_at')) {
|
|
285
|
+
try {
|
|
286
|
+
const trgTemplatePath = (0, path_1.resolve)(__dirname, '../../../templates/database/trg_touch_updated_at.sql.ejs');
|
|
287
|
+
if (await this.fileSystem.exists(trgTemplatePath)) {
|
|
288
|
+
let trgTemplateContent = await this.fileSystem.readFileContent(trgTemplatePath);
|
|
289
|
+
// Simple variable replacement for <%= tableName %>
|
|
290
|
+
trgTemplateContent = trgTemplateContent.replace(/<%=\s*tableName\s*%>/g, table.name);
|
|
291
|
+
migrationContent.push(`-- Trigger to auto-update updated_at for table ${table.name}`, trgTemplateContent);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
console.warn(chalk.yellow(`Template not found: ${trgTemplatePath}. Skipping trg_touch_updated_at trigger for ${table.name}.`));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
console.error(chalk.red(`Error loading trg_touch_updated_at.sql.ejs template: ${error.message}`));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
else if (dbType === 'mysql') {
|
|
303
|
+
console.warn('TO DO: MySQL table creation');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
addUniqueConstraints(tables, dbType, migrationContent) {
|
|
308
|
+
for (const table of tables) {
|
|
309
|
+
if (dbType === 'postgres') {
|
|
310
|
+
this.log(`Creating unique constraints for table: ${table.name}`);
|
|
311
|
+
const uniqueColumns = table.columns.filter((c) => c.isUnique);
|
|
312
|
+
if (uniqueColumns.length) {
|
|
313
|
+
migrationContent.push(`CREATE UNIQUE INDEX "${table.name}_${uniqueColumns.map((c) => c.name).join('_')}_unique" ON "${table.name}" (${uniqueColumns.map((c) => `"${c.name}"`).join(', ')});`, ``);
|
|
314
|
+
}
|
|
315
|
+
for (const column of table.columns) {
|
|
316
|
+
if (column.type === 'slug') {
|
|
317
|
+
this.log(`Adding unique constraint for column: ${column.name}`);
|
|
318
|
+
migrationContent.push(`CREATE UNIQUE INDEX "${table.name}_${column.name}_unique" ON "${table.name}" ("${column.name}");`, ``);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (dbType === 'mysql') {
|
|
323
|
+
console.warn('TO DO: MySQL unique constraints');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
addForeignKeys(tables, dbType, migrationContent) {
|
|
328
|
+
for (const table of tables) {
|
|
329
|
+
this.log(`Processing foreign keys for table: ${table.name}`);
|
|
330
|
+
if (dbType === 'postgres') {
|
|
331
|
+
const foreignKeys = [];
|
|
332
|
+
for (const column of table.columns) {
|
|
333
|
+
if (column.type === 'fk' && column.references) {
|
|
334
|
+
const { table: fkTable, column: fkColumn, onDelete, onUpdate, } = column.references;
|
|
335
|
+
this.log(`Adding foreign key for column: ${column.name} referencing ${fkTable}.${fkColumn}`);
|
|
336
|
+
foreignKeys.push(`ALTER TABLE "${table.name}" ADD CONSTRAINT "${fkTable}_${column.name}_fkey" FOREIGN KEY ("${column.name}") REFERENCES "${fkTable}" ("${fkColumn}") ON DELETE ${onDelete || 'NO ACTION'} ON UPDATE ${onUpdate || 'NO ACTION'};`, ``);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (foreignKeys.length > 0) {
|
|
340
|
+
migrationContent.push(...foreignKeys);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else if (dbType === 'mysql') {
|
|
344
|
+
console.warn('TO DO: MySQL foreign keys');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
getColumnList(row, table) {
|
|
349
|
+
return Object.keys(row)
|
|
350
|
+
.filter((col) => table.columns.some((c) => c.name === col))
|
|
351
|
+
.map((col) => `"${col}"`);
|
|
352
|
+
}
|
|
353
|
+
async getValuesList(row, table, tables) {
|
|
354
|
+
const values = Object.keys(row).filter((col) => table.columns.some((c) => c.name === col));
|
|
355
|
+
const resolvedValues = [];
|
|
356
|
+
for (const col of values) {
|
|
357
|
+
if (typeof row[col] === 'object') {
|
|
358
|
+
if (row[col]['where']) {
|
|
359
|
+
const tableNameRelation = table.columns.find((c) => c.name === col && c.type === 'fk')?.references?.table;
|
|
360
|
+
const tableRelation = tables.find((t) => t.name === tableNameRelation);
|
|
361
|
+
const whereClause = this.getWhereString(row[col]);
|
|
362
|
+
row[col] =
|
|
363
|
+
`(SELECT "${tableRelation?.pk}" FROM "${tableRelation?.name}" ${whereClause})`;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
row[col] = `'${JSON.stringify(row[col])}'`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
const columnDef = table.columns.find((c) => c.name === col);
|
|
371
|
+
if (columnDef?.isNullable && row[col] === null) {
|
|
372
|
+
row[col] = 'NULL';
|
|
373
|
+
}
|
|
374
|
+
else if (!columnDef?.isNullable &&
|
|
375
|
+
(row[col] === undefined || row[col] === null || row[col] === '')) {
|
|
376
|
+
row[col] =
|
|
377
|
+
columnDef?.default !== undefined
|
|
378
|
+
? this.getDefaultValue(columnDef)
|
|
379
|
+
: 'NULL';
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
row[col] = `\$\$${String(row[col]).replace(/'/g, "''")}\$\$`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
resolvedValues.push(row[col]);
|
|
386
|
+
}
|
|
387
|
+
return resolvedValues;
|
|
388
|
+
}
|
|
389
|
+
async getInfoFromRow(row, tables, table) {
|
|
390
|
+
const columnsList = this.getColumnList(row, table).join(', ');
|
|
391
|
+
const valuesList = await this.getValuesList(row, table, tables);
|
|
392
|
+
const valuesString = valuesList.join(', ');
|
|
393
|
+
const hasRelations = 'relations' in row;
|
|
394
|
+
const localeColumns = [];
|
|
395
|
+
const localeValues = [];
|
|
396
|
+
const localeCodes = [];
|
|
397
|
+
let hasValueLocale = false;
|
|
398
|
+
for (const colName in row) {
|
|
399
|
+
if (this.checkIsObjectLocale(row[colName])) {
|
|
400
|
+
localeColumns.push(colName);
|
|
401
|
+
localeValues.push(row[colName]);
|
|
402
|
+
for (const code of Object.keys(row[colName])) {
|
|
403
|
+
if (!localeCodes.includes(code)) {
|
|
404
|
+
localeCodes.push(code);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
hasValueLocale = true;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
columnsList,
|
|
412
|
+
valuesList,
|
|
413
|
+
valuesString,
|
|
414
|
+
hasRelations,
|
|
415
|
+
localeColumns,
|
|
416
|
+
localeValues,
|
|
417
|
+
localeCodes,
|
|
418
|
+
hasValueLocale,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async processRelationsRecursive(relationRow, relationTable, parentTableName, parentAsName, tables, name, cwd, deps, insertTables, relationIndex) {
|
|
422
|
+
const withClauses = [];
|
|
423
|
+
const relatedTableDef = tables.find((t) => t.name === relationTable);
|
|
424
|
+
const tableIntermediate = await this.tableService.getTableIntermediate(parentTableName, relationTable, cwd);
|
|
425
|
+
const relationColumns = [
|
|
426
|
+
...this.getColumnList(relationRow, relatedTableDef),
|
|
427
|
+
];
|
|
428
|
+
let relationQuery = '';
|
|
429
|
+
const relationQuerys = [];
|
|
430
|
+
if (tableIntermediate?.intermediateTable) {
|
|
431
|
+
const tb = await this.tableService.getTableFromYaml(relationTable, cwd);
|
|
432
|
+
const { localeColumns, localeValues, localeCodes } = await this.getInfoFromRow(relationRow, tables, tb);
|
|
433
|
+
const tbPk = tb.columns.find((c) => c.isPrimaryKey || c.type === 'pk')?.name ||
|
|
434
|
+
'id';
|
|
435
|
+
const fkForMain = tableIntermediate.table.columns.find((c) => c.type === 'fk' && c.references.table === parentTableName)?.name;
|
|
436
|
+
const fkForRelation = tableIntermediate.table.columns.find((c) => c.type === 'fk' && c.references.table === relationTable)?.name;
|
|
437
|
+
relationColumns.unshift(`"${fkForMain}"`);
|
|
438
|
+
relationColumns.unshift(`"${fkForRelation}"`);
|
|
439
|
+
const whereClause = this.getWhereString(relationRow);
|
|
440
|
+
deps.push(relationTable);
|
|
441
|
+
insertTables.push(tableIntermediate.name);
|
|
442
|
+
const asName = `insert__${tableIntermediate.name}__for_${parentTableName}__and__${relationTable}__${relationIndex.value++}`;
|
|
443
|
+
relationQuery = `${asName} AS (\n INSERT INTO "${tableIntermediate.name}" (${relationColumns.join(', ')}) SELECT (SELECT "${tbPk}" FROM "${relationTable}" ${whereClause} LIMIT 1), id FROM ${parentAsName} RETURNING id\n)`;
|
|
444
|
+
relationQuerys.push(...(await this.addWithClausesLocales(tb, tables, localeCodes, localeColumns, localeValues, asName)));
|
|
445
|
+
withClauses.push(relationQuery, ...relationQuerys);
|
|
446
|
+
// Process nested relations recursively
|
|
447
|
+
if (relationRow.relations && typeof relationRow.relations === 'object') {
|
|
448
|
+
for (const [nestedRelationTable, nestedRelationRows] of Object.entries(relationRow.relations)) {
|
|
449
|
+
if (Array.isArray(nestedRelationRows) &&
|
|
450
|
+
nestedRelationRows.length > 0) {
|
|
451
|
+
for (const nestedRelationRow of nestedRelationRows) {
|
|
452
|
+
const nestedClauses = await this.processRelationsRecursive(nestedRelationRow, nestedRelationTable, relationTable, asName, tables, name, cwd, deps, insertTables, relationIndex);
|
|
453
|
+
withClauses.push(...nestedClauses);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
const fkForMain = tableIntermediate?.table.columns.find((c) => c.type === 'fk' && c.references.table === parentTableName)?.name;
|
|
461
|
+
relationColumns.unshift(`"${fkForMain}"`);
|
|
462
|
+
const relationValuesList = await this.getValuesList(relationRow, relatedTableDef, tables);
|
|
463
|
+
relationValuesList.unshift(`(SELECT id FROM ${parentAsName})`);
|
|
464
|
+
insertTables.push(relationTable);
|
|
465
|
+
const asName = `insert__${relationTable}__for__${parentTableName}__${relationIndex.value++}`;
|
|
466
|
+
relationQuery = `${asName} AS (\n INSERT INTO "${relationTable}" (${relationColumns.join(', ')}) VALUES (${relationValuesList.join(', ')}) RETURNING id\n)`;
|
|
467
|
+
const tb = tables.find((t) => t.name === relationTable);
|
|
468
|
+
if (tb) {
|
|
469
|
+
const { localeColumns, localeValues, localeCodes } = await this.getInfoFromRow(relationRow, tables, tb);
|
|
470
|
+
relationQuerys.push(...(await this.addWithClausesLocales(tb, tables, localeCodes, localeColumns, localeValues, asName)));
|
|
471
|
+
}
|
|
472
|
+
withClauses.push(relationQuery, ...relationQuerys);
|
|
473
|
+
// Process nested relations recursively
|
|
474
|
+
if (relationRow.relations && typeof relationRow.relations === 'object') {
|
|
475
|
+
for (const [nestedRelationTable, nestedRelationRows] of Object.entries(relationRow.relations)) {
|
|
476
|
+
if (Array.isArray(nestedRelationRows) &&
|
|
477
|
+
nestedRelationRows.length > 0) {
|
|
478
|
+
for (const nestedRelationRow of nestedRelationRows) {
|
|
479
|
+
const nestedClauses = await this.processRelationsRecursive(nestedRelationRow, nestedRelationTable, relationTable, asName, tables, name, cwd, deps, insertTables, relationIndex);
|
|
480
|
+
withClauses.push(...nestedClauses);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return withClauses;
|
|
487
|
+
}
|
|
488
|
+
async getInsertQueries(table, data, tables, name, cwd) {
|
|
489
|
+
const queries = [];
|
|
490
|
+
for (const row of data) {
|
|
491
|
+
const { columnsList, valuesList, valuesString, hasRelations, localeColumns, localeValues, localeCodes, hasValueLocale, } = await this.getInfoFromRow(row, tables, table);
|
|
492
|
+
// Extract dependencies from valuesList (queries that include sub-selects)
|
|
493
|
+
const deps = Array.from(new Set(valuesList
|
|
494
|
+
.filter((v) => /\bselect\b.*\bfrom\b/i.test(v))
|
|
495
|
+
.map((v) => {
|
|
496
|
+
const match = v.match(/\bselect\b.*\bfrom\s+([\w.]+)/i);
|
|
497
|
+
return match ? match[1] : v;
|
|
498
|
+
})));
|
|
499
|
+
const insertTables = [table.name];
|
|
500
|
+
const mainInsert = `INSERT INTO "${table.name}" (${columnsList}) VALUES (${valuesString})`;
|
|
501
|
+
let query = '';
|
|
502
|
+
const withClauses = [];
|
|
503
|
+
if (hasRelations || hasValueLocale) {
|
|
504
|
+
// Main insert with RETURNING clause
|
|
505
|
+
withClauses.push(`inserted AS (\n ${mainInsert}\n RETURNING id\n)`);
|
|
506
|
+
if (hasRelations) {
|
|
507
|
+
// Process relation queries recursively
|
|
508
|
+
const relationIndex = { value: 0 };
|
|
509
|
+
const relations = row.relations || {};
|
|
510
|
+
for (const [relationTable, relationRows] of Object.entries(relations)) {
|
|
511
|
+
if (Array.isArray(relationRows) && relationRows.length > 0) {
|
|
512
|
+
for (const relationRow of relationRows) {
|
|
513
|
+
const relationClauses = await this.processRelationsRecursive(relationRow, relationTable, table.name, 'inserted', tables, name, cwd, deps, insertTables, relationIndex);
|
|
514
|
+
withClauses.push(...relationClauses);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
////////////////////////////////////////////////////////////////////
|
|
520
|
+
if (hasValueLocale) {
|
|
521
|
+
withClauses.push(...(await this.addWithClausesLocales(table, tables, localeCodes, localeColumns, localeValues)));
|
|
522
|
+
}
|
|
523
|
+
query = `WITH ${withClauses.join(',\n')}\nSELECT * FROM inserted;`;
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
query = mainInsert + ';';
|
|
527
|
+
}
|
|
528
|
+
queries.push({
|
|
529
|
+
query: query + '\n\n',
|
|
530
|
+
dependencies: Array.from(new Set(deps.filter((d) => insertTables.includes(d)))),
|
|
531
|
+
insertTables: Array.from(new Set(insertTables)),
|
|
532
|
+
});
|
|
533
|
+
this.log(`Prepared insert query for table: ${table.name}`);
|
|
534
|
+
}
|
|
535
|
+
return queries;
|
|
536
|
+
}
|
|
537
|
+
async addWithClausesLocales(table, tables, localeCodes, localeColumns, localeValues, insertedTableName = 'inserted') {
|
|
538
|
+
const tableLocale = tables.find((t) => t.name === `${table.name}_locale`);
|
|
539
|
+
const withClauses = [];
|
|
540
|
+
if (tableLocale) {
|
|
541
|
+
const fkColumnName = tableLocale?.columns.find((c) => c.type === 'fk' && c.references?.table === table.name)?.name;
|
|
542
|
+
const fkLocaleColumnName = tableLocale?.columns.find((c) => c.type === 'fk' && c.references?.table === 'locale')?.name;
|
|
543
|
+
for (const code of localeCodes) {
|
|
544
|
+
withClauses.push(`insert__${tableLocale.name}__${code}__${this.indexWithClauseLocale++} AS (\n
|
|
545
|
+
INSERT INTO "${table.name}_locale" (${fkLocaleColumnName}, ${fkColumnName}, ${localeColumns.map((c) => `"${c}"`).join(', ')})
|
|
546
|
+
VALUES (
|
|
547
|
+
(SELECT "id" FROM "locale" WHERE "code" = '${code}'),
|
|
548
|
+
(SELECT id FROM ${insertedTableName}),
|
|
549
|
+
${localeColumns.map((c) => `\$\$${localeValues[localeColumns.indexOf(c)][code].replaceAll("'", "''")}\$\$`).join(', ')})\n
|
|
550
|
+
RETURNING id\n
|
|
551
|
+
)`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return withClauses;
|
|
555
|
+
}
|
|
556
|
+
checkIsObjectLocale(value) {
|
|
557
|
+
const validLocales = ['pt', 'en', 'es', 'it', 'jp'];
|
|
558
|
+
return (typeof value === 'object' &&
|
|
559
|
+
value !== null &&
|
|
560
|
+
!Array.isArray(value) &&
|
|
561
|
+
Object.keys(value).length > 0 &&
|
|
562
|
+
Object.keys(value).every((key) => typeof key === 'string' &&
|
|
563
|
+
/^[a-z]{2,3}$/.test(key) &&
|
|
564
|
+
validLocales.includes(key) &&
|
|
565
|
+
(typeof value[key] === 'string' || typeof value[key] === 'number')));
|
|
566
|
+
}
|
|
567
|
+
async getDataFromTable(table, name, cwd) {
|
|
568
|
+
const dataPath = (0, path_1.resolve)(cwd, `./libraries/${name}/hedhog/data`);
|
|
569
|
+
const fileNames = [`${table.name}.yaml`, `${table.name}.yml`];
|
|
570
|
+
let dataFilePath = null;
|
|
571
|
+
for (const fileName of fileNames) {
|
|
572
|
+
const fullPath = (0, path_1.join)(dataPath, fileName);
|
|
573
|
+
if (await this.fileSystem.exists(fullPath)) {
|
|
574
|
+
dataFilePath = fullPath;
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
if (dataFilePath) {
|
|
579
|
+
this.log(`Loading initial data for table: ${table.name}`);
|
|
580
|
+
return await this.fileSystem.readYamlFile(dataFilePath);
|
|
581
|
+
}
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
async afterQueries(name, cwd, migrationContent) {
|
|
585
|
+
this.log(`Running after queries for ${name}...`);
|
|
586
|
+
const queriesPath = (0, path_1.resolve)(cwd, `./libraries/${name}/hedhog/query`);
|
|
587
|
+
try {
|
|
588
|
+
if (await this.fileSystem.exists(queriesPath)) {
|
|
589
|
+
const allFiles = await this.fileSystem.listFiles(queriesPath, () => true);
|
|
590
|
+
for (const file of allFiles) {
|
|
591
|
+
if (file.endsWith('.sql')) {
|
|
592
|
+
const sqlContent = await this.fileSystem.readFileContent((0, path_1.join)(queriesPath, file));
|
|
593
|
+
migrationContent.push(`-- AfterQuery: ${file}`, sqlContent, '');
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
console.warn(chalk.yellow(`Arquivo ignorado no diretório de queries: ${file}`));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
this.log(`No queries directory found at ${queriesPath}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
catch (error) {
|
|
605
|
+
this.log(`Error processing after queries: ${error.message}`);
|
|
606
|
+
// Opcional: podrías relanzar el error si quieres que el flujo principal lo maneje
|
|
607
|
+
// throw error;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
async insertInitialData(tables, name, cwd, migrationContent) {
|
|
611
|
+
const queries = [];
|
|
612
|
+
for (const table of tables) {
|
|
613
|
+
this.log(`Processing initial data for table: ${table.name}`);
|
|
614
|
+
const data = await this.getDataFromTable(table, name, cwd);
|
|
615
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
616
|
+
// Sort rows so that rows with relation queries come last
|
|
617
|
+
const sortedData = [
|
|
618
|
+
...data.filter((row) => !Object.values(row).some((v) => typeof v === 'object' && v !== null && 'where' in v)),
|
|
619
|
+
...data.filter((row) => Object.values(row).some((v) => typeof v === 'object' && v !== null && 'where' in v)),
|
|
620
|
+
];
|
|
621
|
+
queries.push(...(await this.getInsertQueries(table, sortedData, tables, name, cwd)));
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
this.log(`Data for table ${table.name} is not an array. Skipping insertion.`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (queries.length) {
|
|
628
|
+
migrationContent.push(`-- InsertInitialData`);
|
|
629
|
+
}
|
|
630
|
+
// Order queries by dependencies
|
|
631
|
+
const orderedQueries = this.orderQueriesByDependencies(queries);
|
|
632
|
+
migrationContent.push(...orderedQueries.map((q) => q.query));
|
|
633
|
+
}
|
|
634
|
+
orderQueriesByDependencies(queries) {
|
|
635
|
+
const orderedQueries = [];
|
|
636
|
+
const remainingQueries = [...queries];
|
|
637
|
+
const insertedTables = new Set();
|
|
638
|
+
while (remainingQueries.length) {
|
|
639
|
+
const notReady = remainingQueries.filter((q) => !q.dependencies.every((dep) => insertedTables.has(dep)));
|
|
640
|
+
const ready = remainingQueries.filter((q) => q.dependencies.every((dep) => insertedTables.has(dep)));
|
|
641
|
+
let candidate;
|
|
642
|
+
if (notReady.length) {
|
|
643
|
+
notReady.sort((a, b) => {
|
|
644
|
+
const diffDeps = a.dependencies.length - b.dependencies.length;
|
|
645
|
+
if (diffDeps !== 0)
|
|
646
|
+
return diffDeps;
|
|
647
|
+
return (a.insertTables?.length || 0) - (b.insertTables?.length || 0);
|
|
648
|
+
});
|
|
649
|
+
candidate = notReady[0];
|
|
650
|
+
}
|
|
651
|
+
else if (ready.length) {
|
|
652
|
+
ready.sort((a, b) => {
|
|
653
|
+
const diffDeps = a.dependencies.length - b.dependencies.length;
|
|
654
|
+
if (diffDeps !== 0)
|
|
655
|
+
return diffDeps;
|
|
656
|
+
return (a.insertTables?.length || 0) - (b.insertTables?.length || 0);
|
|
657
|
+
});
|
|
658
|
+
candidate = ready[0];
|
|
659
|
+
}
|
|
660
|
+
if (candidate) {
|
|
661
|
+
orderedQueries.push(candidate);
|
|
662
|
+
candidate.insertTables?.forEach((table) => insertedTables.add(table));
|
|
663
|
+
const index = remainingQueries.indexOf(candidate);
|
|
664
|
+
if (index !== -1)
|
|
665
|
+
remainingQueries.splice(index, 1);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
console.warn('Could not order all queries; remaining will be added at the end.');
|
|
669
|
+
orderedQueries.push(...remainingQueries);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return orderedQueries;
|
|
674
|
+
}
|
|
675
|
+
toSnackCase(str) {
|
|
676
|
+
return str
|
|
677
|
+
.split(/[-_ ]/)
|
|
678
|
+
.map((word) => word.toLowerCase())
|
|
679
|
+
.join('_');
|
|
680
|
+
}
|
|
681
|
+
async createMigrations(name, cwd = process.cwd(), verbose = false) {
|
|
682
|
+
this.verbose = verbose;
|
|
683
|
+
const allTables = [];
|
|
684
|
+
const tables = await this.tableService.loadTablesFromYaml(name, cwd, verbose);
|
|
685
|
+
const tablesDep = await this.tableService.getTablesFromDependencies(name, cwd);
|
|
686
|
+
allTables.push(...tablesDep.filter((t) => !tables.find((tbl) => tbl.name === t.name)));
|
|
687
|
+
const uniqueTablesMap = new Map();
|
|
688
|
+
[...allTables, ...tables].forEach((table) => {
|
|
689
|
+
if (!uniqueTablesMap.has(table.name)) {
|
|
690
|
+
uniqueTablesMap.set(table.name, table);
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
allTables.length = 0;
|
|
694
|
+
allTables.push(...uniqueTablesMap.values());
|
|
695
|
+
if (!tables || tables.length === 0) {
|
|
696
|
+
this.log(`No tables found for ${name}. Skipping migration creation.`);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
this.log(`Creating migrations for ${name}...`);
|
|
700
|
+
const dbType = await this.database.getDatabaseType(cwd);
|
|
701
|
+
if (!dbType) {
|
|
702
|
+
throw new Error('Database type could not be determined');
|
|
703
|
+
}
|
|
704
|
+
const migrationContent = [];
|
|
705
|
+
const createdTables = [];
|
|
706
|
+
this.log(`Database type detected: ${dbType}`);
|
|
707
|
+
this.log(`Processing enums for ${name}...`);
|
|
708
|
+
this.processEnums(tables, dbType, migrationContent);
|
|
709
|
+
this.log(`Processed enums for ${name}!`);
|
|
710
|
+
this.log(`Creating tables for ${name}...`);
|
|
711
|
+
await this.createTableMigrations(tables, dbType, migrationContent, createdTables, cwd);
|
|
712
|
+
this.log(`Created tables for ${name}!`);
|
|
713
|
+
this.log(`Adding unique constraints for ${name}...`);
|
|
714
|
+
this.addUniqueConstraints(tables, dbType, migrationContent);
|
|
715
|
+
this.log(`Added unique constraints for ${name}!`);
|
|
716
|
+
this.log(`Adding foreign keys for ${name}...`);
|
|
717
|
+
this.addForeignKeys(tables, dbType, migrationContent);
|
|
718
|
+
this.log(`Added foreign keys for ${name}!`);
|
|
719
|
+
this.log(`Inserting initial data for ${name}...`);
|
|
720
|
+
await this.insertInitialData(!allTables.length && tables.length ? tables : allTables, name, cwd, migrationContent);
|
|
721
|
+
this.log(`Inserted initial data for ${name}!`);
|
|
722
|
+
this.log(`Running after querys for ${name}...`);
|
|
723
|
+
await this.afterQueries(name, cwd, migrationContent);
|
|
724
|
+
this.log(`Completed after querys for ${name}!`);
|
|
725
|
+
// Save migration file
|
|
726
|
+
const timestamp = new Date()
|
|
727
|
+
.toISOString()
|
|
728
|
+
.replace(/[-:.T]/g, '')
|
|
729
|
+
.slice(0, 14);
|
|
730
|
+
const migrationFileName = `${timestamp}_${this.toSnackCase(name)}`;
|
|
731
|
+
this.log(`Creating migration file: ${migrationFileName}`);
|
|
732
|
+
const migrationFilePath = (0, path_1.resolve)(cwd, `./apps/api/prisma/migrations/${migrationFileName}`);
|
|
733
|
+
this.log(`Creating migration directory at: ${migrationFilePath}`);
|
|
734
|
+
await this.fileSystem.createDirectory(migrationFilePath);
|
|
735
|
+
const contentSQL = migrationContent.join('\n') + '\n';
|
|
736
|
+
const formattedSQL = (0, sql_formatter_1.format)(contentSQL, {
|
|
737
|
+
language: 'postgresql',
|
|
738
|
+
newlineBeforeSemicolon: false,
|
|
739
|
+
});
|
|
740
|
+
await this.fileSystem.writeFileContent((0, path_1.resolve)(migrationFilePath, 'migration.sql'), formattedSQL);
|
|
741
|
+
// Update hedhog file
|
|
742
|
+
const hedhogFilePath = (0, path_1.join)(cwd, 'hedhog.json');
|
|
743
|
+
let hedhogFile = {};
|
|
744
|
+
if (await this.fileSystem.exists(hedhogFilePath)) {
|
|
745
|
+
hedhogFile = await this.fileSystem.readJsonFile(hedhogFilePath);
|
|
746
|
+
}
|
|
747
|
+
if (!hedhogFile.libraries) {
|
|
748
|
+
hedhogFile.libraries = {};
|
|
749
|
+
}
|
|
750
|
+
const hashLib = await this.developer.hashDirectory((0, path_1.resolve)(cwd, `./libraries/${name}`));
|
|
751
|
+
hedhogFile.libraries[name] = {
|
|
752
|
+
hash: hashLib,
|
|
753
|
+
migration: migrationFileName,
|
|
754
|
+
};
|
|
755
|
+
await this.fileSystem.writeJsonFile(hedhogFilePath, hedhogFile);
|
|
756
|
+
this.log(`Migration file ${migrationFileName} created successfully.`);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
exports.MigrationService = MigrationService;
|
|
760
|
+
exports.MigrationService = MigrationService = __decorate([
|
|
761
|
+
(0, common_1.Injectable)(),
|
|
762
|
+
__metadata("design:paramtypes", [database_service_1.DatabaseService,
|
|
763
|
+
developer_service_1.DeveloperService,
|
|
764
|
+
file_system_service_1.FileSystemService,
|
|
765
|
+
table_service_1.TableService])
|
|
766
|
+
], MigrationService);
|
|
767
|
+
//# sourceMappingURL=migration.service.js.map
|