@javalabs/prisma-client 1.0.18 → 1.0.19
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/.dockerignore +14 -0
- package/Dockerfile +23 -0
- package/README.md +269 -269
- package/dist/index.d.ts +1 -1
- package/dist/prisma.service.d.ts +1 -1
- package/dist/scripts/add-uuid-to-table.js +32 -32
- package/dist/scripts/data-migration/batch-migrator.js +12 -12
- package/dist/scripts/data-migration/data-transformer.js +14 -14
- package/dist/scripts/data-migration/dependency-resolver.js +23 -23
- package/dist/scripts/data-migration/entity-discovery.js +68 -68
- package/dist/scripts/data-migration/foreign-key-manager.js +23 -23
- package/dist/scripts/data-migration/migration-tool.js +5 -5
- package/dist/scripts/data-migration/schema-utils.js +74 -74
- package/dist/scripts/data-migration/typecast-manager.js +4 -4
- package/dist/scripts/database-initializer.js +5 -5
- package/dist/scripts/drop-database.js +5 -5
- package/dist/scripts/fix-data-types.js +53 -53
- package/dist/scripts/fix-enum-values.js +34 -34
- package/dist/scripts/fix-schema-discrepancies.js +40 -40
- package/dist/scripts/fix-table-indexes.js +81 -81
- package/dist/scripts/migrate-schema-structure.js +4 -4
- package/dist/scripts/migrate-uuid.js +19 -19
- package/dist/scripts/post-migration-validator.js +49 -49
- package/dist/scripts/pre-migration-validator.js +107 -107
- package/dist/scripts/reset-database.js +21 -21
- package/dist/scripts/retry-failed-migrations.js +28 -28
- package/dist/scripts/run-migration.js +5 -5
- package/dist/scripts/schema-sync.js +18 -18
- package/dist/scripts/sequence-sync-cli.js +55 -55
- package/dist/scripts/sequence-synchronizer.js +20 -20
- package/dist/scripts/sync-enum-types.js +30 -30
- package/dist/scripts/sync-enum-values.js +52 -52
- package/dist/scripts/truncate-database.js +10 -10
- package/dist/scripts/verify-migration-setup.js +10 -10
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/migration-config.json +63 -63
- package/migration-config.json.bk +95 -95
- package/package.json +44 -44
- package/prisma/migrations/add_accepts_partial_payments_to_users.sql +19 -0
- package/prisma/migrations/add_amount_received_to_manual_payments.sql +19 -0
- package/prisma/migrations/add_commission_fields.sql +33 -0
- package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
- package/prisma/migrations/complete_partial_payments_migration.sql +53 -0
- package/prisma/migrations/create_settlements_table.sql +60 -0
- package/prisma/schema.prisma +47 -1
- package/src/index.ts +23 -23
- package/src/prisma-factory.service.ts +40 -40
- package/src/prisma.module.ts +9 -9
- package/src/prisma.service.ts +16 -16
- package/src/scripts/add-uuid-to-table.ts +138 -138
- package/src/scripts/create-tenant-schemas.ts +145 -145
- package/src/scripts/data-migration/batch-migrator.ts +248 -248
- package/src/scripts/data-migration/data-transformer.ts +426 -426
- package/src/scripts/data-migration/db-connector.ts +120 -120
- package/src/scripts/data-migration/dependency-resolver.ts +174 -174
- package/src/scripts/data-migration/entity-discovery.ts +196 -196
- package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
- package/src/scripts/data-migration/migration-config.json +63 -63
- package/src/scripts/data-migration/migration-tool.ts +509 -509
- package/src/scripts/data-migration/schema-utils.ts +248 -248
- package/src/scripts/data-migration/tenant-migrator.ts +201 -201
- package/src/scripts/data-migration/typecast-manager.ts +193 -193
- package/src/scripts/data-migration/types.ts +113 -113
- package/src/scripts/database-initializer.ts +49 -49
- package/src/scripts/drop-database.ts +104 -104
- package/src/scripts/dump-source-db.sh +61 -61
- package/src/scripts/encrypt-user-passwords.ts +36 -36
- package/src/scripts/error-handler.ts +117 -117
- package/src/scripts/fix-data-types.ts +241 -241
- package/src/scripts/fix-enum-values.ts +357 -357
- package/src/scripts/fix-schema-discrepancies.ts +317 -317
- package/src/scripts/fix-table-indexes.ts +601 -601
- package/src/scripts/migrate-schema-structure.ts +90 -90
- package/src/scripts/migrate-uuid.ts +76 -76
- package/src/scripts/post-migration-validator.ts +526 -526
- package/src/scripts/pre-migration-validator.ts +610 -610
- package/src/scripts/reset-database.ts +263 -263
- package/src/scripts/retry-failed-migrations.ts +416 -416
- package/src/scripts/run-migration.ts +707 -707
- package/src/scripts/schema-sync.ts +128 -128
- package/src/scripts/sequence-sync-cli.ts +416 -416
- package/src/scripts/sequence-synchronizer.ts +127 -127
- package/src/scripts/sync-enum-types.ts +170 -170
- package/src/scripts/sync-enum-values.ts +563 -563
- package/src/scripts/truncate-database.ts +123 -123
- package/src/scripts/verify-migration-setup.ts +135 -135
- package/tsconfig.json +17 -17
- package/dist/scripts/data-migration/dependency-manager.d.ts +0 -9
- package/dist/scripts/data-migration/dependency-manager.js +0 -86
- package/dist/scripts/data-migration/dependency-manager.js.map +0 -1
- package/dist/scripts/data-migration/migration-config.json +0 -63
- package/dist/scripts/data-migration/migration-phases.d.ts +0 -5
- package/dist/scripts/data-migration/migration-phases.js +0 -55
- package/dist/scripts/data-migration/migration-phases.js.map +0 -1
- package/dist/scripts/data-migration/multi-source-migrator.d.ts +0 -17
- package/dist/scripts/data-migration/multi-source-migrator.js +0 -130
- package/dist/scripts/data-migration/multi-source-migrator.js.map +0 -1
- package/dist/scripts/data-migration/phase-generator.d.ts +0 -15
- package/dist/scripts/data-migration/phase-generator.js +0 -187
- package/dist/scripts/data-migration/phase-generator.js.map +0 -1
- package/dist/scripts/data-migration.d.ts +0 -22
- package/dist/scripts/data-migration.js +0 -593
- package/dist/scripts/data-migration.js.map +0 -1
- package/dist/scripts/multi-db-migration.d.ts +0 -1
- package/dist/scripts/multi-db-migration.js +0 -55
- package/dist/scripts/multi-db-migration.js.map +0 -1
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
import { PrismaClient } from "@prisma/client";
|
|
2
|
-
import * as pg from "pg";
|
|
3
|
-
import * as dotenv from "dotenv";
|
|
4
|
-
import { DatabaseConnections, DatabaseConnection } from "./types";
|
|
5
|
-
|
|
6
|
-
dotenv.config();
|
|
7
|
-
|
|
8
|
-
export interface SourceDatabase {
|
|
9
|
-
id: string;
|
|
10
|
-
url: string;
|
|
11
|
-
name: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class DatabaseConnector {
|
|
15
|
-
static createConnections(
|
|
16
|
-
sourceDatabases?: SourceDatabase[]
|
|
17
|
-
): DatabaseConnections {
|
|
18
|
-
// Crear conexiones para cada base de datos de origen
|
|
19
|
-
const sourceConnections: DatabaseConnection[] = (sourceDatabases || []).map(
|
|
20
|
-
(source): DatabaseConnection => {
|
|
21
|
-
// Conexión de pool para la base de datos de origen
|
|
22
|
-
const pool = new pg.Pool({
|
|
23
|
-
connectionString: source.url,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Cliente Prisma para la base de datos de origen
|
|
27
|
-
const prisma = new PrismaClient({
|
|
28
|
-
datasources: {
|
|
29
|
-
db: {
|
|
30
|
-
url: source.url,
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const connection: DatabaseConnection = {
|
|
36
|
-
pool,
|
|
37
|
-
prisma,
|
|
38
|
-
sourceId: source.id,
|
|
39
|
-
async query(sql: string, params?: any[]) {
|
|
40
|
-
return pool.query(sql, params);
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
return connection;
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const sourcePool = new pg.Pool({
|
|
49
|
-
connectionString: process.env.SOURCE_DATABASE_URL,
|
|
50
|
-
max: 20,
|
|
51
|
-
idleTimeoutMillis: 30000,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Source Prisma client
|
|
55
|
-
const sourcePrisma = new PrismaClient({
|
|
56
|
-
datasources: {
|
|
57
|
-
db: {
|
|
58
|
-
url: process.env.SOURCE_DATABASE_URL,
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Target database connection (our multi-tenant database)
|
|
64
|
-
const targetPool = new pg.Pool({
|
|
65
|
-
connectionString: process.env.DATABASE_URL,
|
|
66
|
-
max: 20,
|
|
67
|
-
idleTimeoutMillis: 30000,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Target Prisma client
|
|
71
|
-
const targetPrisma = new PrismaClient({
|
|
72
|
-
datasources: {
|
|
73
|
-
db: {
|
|
74
|
-
url: process.env.DATABASE_URL,
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
sourceConnections,
|
|
81
|
-
targetPrisma,
|
|
82
|
-
targetPool,
|
|
83
|
-
sourcePool,
|
|
84
|
-
sourcePrisma,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
static createTenantPrismaClient(tenantId: string): PrismaClient {
|
|
89
|
-
return new PrismaClient({
|
|
90
|
-
datasources: {
|
|
91
|
-
db: {
|
|
92
|
-
url: `${process.env.DATABASE_URL}?schema=${tenantId}`,
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
// Increase transaction timeout to 2 minutes
|
|
96
|
-
transactionOptions: {
|
|
97
|
-
maxWait: 120000, // 2 minutes max wait time
|
|
98
|
-
timeout: 120000, // 2 minutes timeout
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
static async cleanup(connections: DatabaseConnections): Promise<void> {
|
|
104
|
-
// Desconectar todas las conexiones de origen
|
|
105
|
-
if (connections.sourceConnections) {
|
|
106
|
-
for (const sourceConn of connections.sourceConnections) {
|
|
107
|
-
if (sourceConn.pool) await sourceConn.pool.end();
|
|
108
|
-
if (sourceConn.prisma) await sourceConn.prisma.$disconnect();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Desconectar conexiones de destino
|
|
113
|
-
await connections.targetPrisma.$disconnect();
|
|
114
|
-
await connections.targetPool.end();
|
|
115
|
-
|
|
116
|
-
// Desconectar conexiones principales de origen si existen
|
|
117
|
-
if (connections.sourcePrisma) await connections.sourcePrisma.$disconnect();
|
|
118
|
-
if (connections.sourcePool) await connections.sourcePool.end();
|
|
119
|
-
}
|
|
120
|
-
}
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
import * as pg from "pg";
|
|
3
|
+
import * as dotenv from "dotenv";
|
|
4
|
+
import { DatabaseConnections, DatabaseConnection } from "./types";
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
export interface SourceDatabase {
|
|
9
|
+
id: string;
|
|
10
|
+
url: string;
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class DatabaseConnector {
|
|
15
|
+
static createConnections(
|
|
16
|
+
sourceDatabases?: SourceDatabase[]
|
|
17
|
+
): DatabaseConnections {
|
|
18
|
+
// Crear conexiones para cada base de datos de origen
|
|
19
|
+
const sourceConnections: DatabaseConnection[] = (sourceDatabases || []).map(
|
|
20
|
+
(source): DatabaseConnection => {
|
|
21
|
+
// Conexión de pool para la base de datos de origen
|
|
22
|
+
const pool = new pg.Pool({
|
|
23
|
+
connectionString: source.url,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Cliente Prisma para la base de datos de origen
|
|
27
|
+
const prisma = new PrismaClient({
|
|
28
|
+
datasources: {
|
|
29
|
+
db: {
|
|
30
|
+
url: source.url,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const connection: DatabaseConnection = {
|
|
36
|
+
pool,
|
|
37
|
+
prisma,
|
|
38
|
+
sourceId: source.id,
|
|
39
|
+
async query(sql: string, params?: any[]) {
|
|
40
|
+
return pool.query(sql, params);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return connection;
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const sourcePool = new pg.Pool({
|
|
49
|
+
connectionString: process.env.SOURCE_DATABASE_URL,
|
|
50
|
+
max: 20,
|
|
51
|
+
idleTimeoutMillis: 30000,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Source Prisma client
|
|
55
|
+
const sourcePrisma = new PrismaClient({
|
|
56
|
+
datasources: {
|
|
57
|
+
db: {
|
|
58
|
+
url: process.env.SOURCE_DATABASE_URL,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Target database connection (our multi-tenant database)
|
|
64
|
+
const targetPool = new pg.Pool({
|
|
65
|
+
connectionString: process.env.DATABASE_URL,
|
|
66
|
+
max: 20,
|
|
67
|
+
idleTimeoutMillis: 30000,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Target Prisma client
|
|
71
|
+
const targetPrisma = new PrismaClient({
|
|
72
|
+
datasources: {
|
|
73
|
+
db: {
|
|
74
|
+
url: process.env.DATABASE_URL,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
sourceConnections,
|
|
81
|
+
targetPrisma,
|
|
82
|
+
targetPool,
|
|
83
|
+
sourcePool,
|
|
84
|
+
sourcePrisma,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static createTenantPrismaClient(tenantId: string): PrismaClient {
|
|
89
|
+
return new PrismaClient({
|
|
90
|
+
datasources: {
|
|
91
|
+
db: {
|
|
92
|
+
url: `${process.env.DATABASE_URL}?schema=${tenantId}`,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
// Increase transaction timeout to 2 minutes
|
|
96
|
+
transactionOptions: {
|
|
97
|
+
maxWait: 120000, // 2 minutes max wait time
|
|
98
|
+
timeout: 120000, // 2 minutes timeout
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static async cleanup(connections: DatabaseConnections): Promise<void> {
|
|
104
|
+
// Desconectar todas las conexiones de origen
|
|
105
|
+
if (connections.sourceConnections) {
|
|
106
|
+
for (const sourceConn of connections.sourceConnections) {
|
|
107
|
+
if (sourceConn.pool) await sourceConn.pool.end();
|
|
108
|
+
if (sourceConn.prisma) await sourceConn.prisma.$disconnect();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Desconectar conexiones de destino
|
|
113
|
+
await connections.targetPrisma.$disconnect();
|
|
114
|
+
await connections.targetPool.end();
|
|
115
|
+
|
|
116
|
+
// Desconectar conexiones principales de origen si existen
|
|
117
|
+
if (connections.sourcePrisma) await connections.sourcePrisma.$disconnect();
|
|
118
|
+
if (connections.sourcePool) await connections.sourcePool.end();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
import { Logger } from "@nestjs/common";
|
|
2
|
-
import { Pool } from "pg";
|
|
3
|
-
|
|
4
|
-
export class DependencyResolver {
|
|
5
|
-
private readonly logger = new Logger("DependencyResolver");
|
|
6
|
-
private readonly visitedTables = new Set<string>();
|
|
7
|
-
private readonly temporaryMark = new Set<string>();
|
|
8
|
-
private readonly tableOrder: string[] = [];
|
|
9
|
-
private config: any;
|
|
10
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
private readonly sourcePool: Pool,
|
|
13
|
-
private readonly targetPool: Pool
|
|
14
|
-
) {}
|
|
15
|
-
|
|
16
|
-
setConfig(config: any) {
|
|
17
|
-
this.config = config;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async analyzeDependencies(): Promise<string[]> {
|
|
21
|
-
try {
|
|
22
|
-
this.logger.log("Iniciando análisis de dependencias...");
|
|
23
|
-
|
|
24
|
-
// Obtener todas las tablas del esquema
|
|
25
|
-
const tables = await this.getAllTables();
|
|
26
|
-
|
|
27
|
-
// Resetear estado
|
|
28
|
-
this.visitedTables.clear();
|
|
29
|
-
this.temporaryMark.clear();
|
|
30
|
-
this.tableOrder.length = 0;
|
|
31
|
-
|
|
32
|
-
// Ordenar tablas por prioridad primero
|
|
33
|
-
const priorityOrder = this.getPriorityOrder(tables);
|
|
34
|
-
|
|
35
|
-
// Realizar ordenamiento topológico para cada grupo de prioridad
|
|
36
|
-
for (const priorityGroup of priorityOrder) {
|
|
37
|
-
for (const table of priorityGroup) {
|
|
38
|
-
if (!this.visitedTables.has(table)) {
|
|
39
|
-
await this.visit(table);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.logger.log(
|
|
45
|
-
`Orden de migración determinado: ${this.tableOrder.join(" -> ")}`
|
|
46
|
-
);
|
|
47
|
-
return this.tableOrder;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
this.logger.error(`Error analizando dependencias: ${error.message}`);
|
|
50
|
-
throw error;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private getPriorityOrder(tables: string[]): string[][] {
|
|
55
|
-
const priorities = {
|
|
56
|
-
high: new Set(this.config?.migrationPriorities?.high || []),
|
|
57
|
-
medium: new Set(this.config?.migrationPriorities?.medium || []),
|
|
58
|
-
low: new Set(this.config?.migrationPriorities?.low || []),
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Agrupar tablas por prioridad
|
|
62
|
-
const highPriority = tables.filter((t) => priorities.high.has(t));
|
|
63
|
-
const mediumPriority = tables.filter((t) => priorities.medium.has(t));
|
|
64
|
-
const lowPriority = tables.filter((t) => priorities.low.has(t));
|
|
65
|
-
const noPriority = tables.filter(
|
|
66
|
-
(t) =>
|
|
67
|
-
!priorities.high.has(t) &&
|
|
68
|
-
!priorities.medium.has(t) &&
|
|
69
|
-
!priorities.low.has(t)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return [highPriority, mediumPriority, noPriority, lowPriority];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private async visit(table: string, path: string[] = []): Promise<void> {
|
|
76
|
-
// Detectar ciclos
|
|
77
|
-
if (this.temporaryMark.has(table)) {
|
|
78
|
-
const cycle = [...path, table].join(" -> ");
|
|
79
|
-
this.logger.warn(`Detectado ciclo de dependencias: ${cycle}`);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (this.visitedTables.has(table)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
this.temporaryMark.add(table);
|
|
88
|
-
path.push(table);
|
|
89
|
-
|
|
90
|
-
// Obtener dependencias tanto de la configuración como de la base de datos
|
|
91
|
-
const dependencies = await this.getTableDependencies(table);
|
|
92
|
-
|
|
93
|
-
for (const dep of dependencies) {
|
|
94
|
-
if (!this.visitedTables.has(dep)) {
|
|
95
|
-
await this.visit(dep, [...path]);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this.temporaryMark.delete(table);
|
|
100
|
-
this.visitedTables.add(table);
|
|
101
|
-
this.tableOrder.push(table);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private async getAllTables(): Promise<string[]> {
|
|
105
|
-
const query = `
|
|
106
|
-
SELECT table_name
|
|
107
|
-
FROM information_schema.tables
|
|
108
|
-
WHERE table_schema = 'public'
|
|
109
|
-
AND table_type = 'BASE TABLE'
|
|
110
|
-
`;
|
|
111
|
-
|
|
112
|
-
const result = await this.sourcePool.query(query);
|
|
113
|
-
return result.rows.map((row) => row.table_name);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private async getTableDependencies(table: string): Promise<string[]> {
|
|
117
|
-
const dependencies = new Set<string>();
|
|
118
|
-
|
|
119
|
-
// Obtener dependencias de la configuración
|
|
120
|
-
const configDeps = this.config?.tables?.[table]?.dependencies || [];
|
|
121
|
-
configDeps.forEach((dep) => dependencies.add(dep));
|
|
122
|
-
|
|
123
|
-
// Obtener dependencias de la base de datos
|
|
124
|
-
const query = `
|
|
125
|
-
SELECT
|
|
126
|
-
ccu.table_name AS foreign_table
|
|
127
|
-
FROM
|
|
128
|
-
information_schema.table_constraints tc
|
|
129
|
-
JOIN information_schema.constraint_column_usage ccu
|
|
130
|
-
ON ccu.constraint_name = tc.constraint_name
|
|
131
|
-
WHERE
|
|
132
|
-
tc.constraint_type = 'FOREIGN KEY'
|
|
133
|
-
AND tc.table_name = $1
|
|
134
|
-
AND tc.table_schema = 'public'
|
|
135
|
-
`;
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
const result = await this.sourcePool.query(query, [table]);
|
|
139
|
-
result.rows.forEach((row) => dependencies.add(row.foreign_table));
|
|
140
|
-
} catch (error) {
|
|
141
|
-
this.logger.warn(
|
|
142
|
-
`Error obteniendo dependencias para ${table}: ${error.message}`
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return Array.from(dependencies);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async validateDependencies(
|
|
150
|
-
table: string,
|
|
151
|
-
dependencies: string[]
|
|
152
|
-
): Promise<boolean> {
|
|
153
|
-
for (const dep of dependencies) {
|
|
154
|
-
const query = `
|
|
155
|
-
SELECT EXISTS (
|
|
156
|
-
SELECT 1
|
|
157
|
-
FROM information_schema.tables
|
|
158
|
-
WHERE table_schema = 'public'
|
|
159
|
-
AND table_name = $1
|
|
160
|
-
)
|
|
161
|
-
`;
|
|
162
|
-
|
|
163
|
-
const result = await this.sourcePool.query(query, [dep]);
|
|
164
|
-
if (!result.rows[0].exists) {
|
|
165
|
-
this.logger.warn(
|
|
166
|
-
`Dependencia ${dep} no encontrada para tabla ${table}`
|
|
167
|
-
);
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
import { Pool } from "pg";
|
|
3
|
+
|
|
4
|
+
export class DependencyResolver {
|
|
5
|
+
private readonly logger = new Logger("DependencyResolver");
|
|
6
|
+
private readonly visitedTables = new Set<string>();
|
|
7
|
+
private readonly temporaryMark = new Set<string>();
|
|
8
|
+
private readonly tableOrder: string[] = [];
|
|
9
|
+
private config: any;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly sourcePool: Pool,
|
|
13
|
+
private readonly targetPool: Pool
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
setConfig(config: any) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async analyzeDependencies(): Promise<string[]> {
|
|
21
|
+
try {
|
|
22
|
+
this.logger.log("Iniciando análisis de dependencias...");
|
|
23
|
+
|
|
24
|
+
// Obtener todas las tablas del esquema
|
|
25
|
+
const tables = await this.getAllTables();
|
|
26
|
+
|
|
27
|
+
// Resetear estado
|
|
28
|
+
this.visitedTables.clear();
|
|
29
|
+
this.temporaryMark.clear();
|
|
30
|
+
this.tableOrder.length = 0;
|
|
31
|
+
|
|
32
|
+
// Ordenar tablas por prioridad primero
|
|
33
|
+
const priorityOrder = this.getPriorityOrder(tables);
|
|
34
|
+
|
|
35
|
+
// Realizar ordenamiento topológico para cada grupo de prioridad
|
|
36
|
+
for (const priorityGroup of priorityOrder) {
|
|
37
|
+
for (const table of priorityGroup) {
|
|
38
|
+
if (!this.visitedTables.has(table)) {
|
|
39
|
+
await this.visit(table);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.logger.log(
|
|
45
|
+
`Orden de migración determinado: ${this.tableOrder.join(" -> ")}`
|
|
46
|
+
);
|
|
47
|
+
return this.tableOrder;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
this.logger.error(`Error analizando dependencias: ${error.message}`);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private getPriorityOrder(tables: string[]): string[][] {
|
|
55
|
+
const priorities = {
|
|
56
|
+
high: new Set(this.config?.migrationPriorities?.high || []),
|
|
57
|
+
medium: new Set(this.config?.migrationPriorities?.medium || []),
|
|
58
|
+
low: new Set(this.config?.migrationPriorities?.low || []),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Agrupar tablas por prioridad
|
|
62
|
+
const highPriority = tables.filter((t) => priorities.high.has(t));
|
|
63
|
+
const mediumPriority = tables.filter((t) => priorities.medium.has(t));
|
|
64
|
+
const lowPriority = tables.filter((t) => priorities.low.has(t));
|
|
65
|
+
const noPriority = tables.filter(
|
|
66
|
+
(t) =>
|
|
67
|
+
!priorities.high.has(t) &&
|
|
68
|
+
!priorities.medium.has(t) &&
|
|
69
|
+
!priorities.low.has(t)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return [highPriority, mediumPriority, noPriority, lowPriority];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async visit(table: string, path: string[] = []): Promise<void> {
|
|
76
|
+
// Detectar ciclos
|
|
77
|
+
if (this.temporaryMark.has(table)) {
|
|
78
|
+
const cycle = [...path, table].join(" -> ");
|
|
79
|
+
this.logger.warn(`Detectado ciclo de dependencias: ${cycle}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this.visitedTables.has(table)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.temporaryMark.add(table);
|
|
88
|
+
path.push(table);
|
|
89
|
+
|
|
90
|
+
// Obtener dependencias tanto de la configuración como de la base de datos
|
|
91
|
+
const dependencies = await this.getTableDependencies(table);
|
|
92
|
+
|
|
93
|
+
for (const dep of dependencies) {
|
|
94
|
+
if (!this.visitedTables.has(dep)) {
|
|
95
|
+
await this.visit(dep, [...path]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.temporaryMark.delete(table);
|
|
100
|
+
this.visitedTables.add(table);
|
|
101
|
+
this.tableOrder.push(table);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private async getAllTables(): Promise<string[]> {
|
|
105
|
+
const query = `
|
|
106
|
+
SELECT table_name
|
|
107
|
+
FROM information_schema.tables
|
|
108
|
+
WHERE table_schema = 'public'
|
|
109
|
+
AND table_type = 'BASE TABLE'
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
const result = await this.sourcePool.query(query);
|
|
113
|
+
return result.rows.map((row) => row.table_name);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async getTableDependencies(table: string): Promise<string[]> {
|
|
117
|
+
const dependencies = new Set<string>();
|
|
118
|
+
|
|
119
|
+
// Obtener dependencias de la configuración
|
|
120
|
+
const configDeps = this.config?.tables?.[table]?.dependencies || [];
|
|
121
|
+
configDeps.forEach((dep) => dependencies.add(dep));
|
|
122
|
+
|
|
123
|
+
// Obtener dependencias de la base de datos
|
|
124
|
+
const query = `
|
|
125
|
+
SELECT
|
|
126
|
+
ccu.table_name AS foreign_table
|
|
127
|
+
FROM
|
|
128
|
+
information_schema.table_constraints tc
|
|
129
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
130
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
131
|
+
WHERE
|
|
132
|
+
tc.constraint_type = 'FOREIGN KEY'
|
|
133
|
+
AND tc.table_name = $1
|
|
134
|
+
AND tc.table_schema = 'public'
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const result = await this.sourcePool.query(query, [table]);
|
|
139
|
+
result.rows.forEach((row) => dependencies.add(row.foreign_table));
|
|
140
|
+
} catch (error) {
|
|
141
|
+
this.logger.warn(
|
|
142
|
+
`Error obteniendo dependencias para ${table}: ${error.message}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return Array.from(dependencies);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async validateDependencies(
|
|
150
|
+
table: string,
|
|
151
|
+
dependencies: string[]
|
|
152
|
+
): Promise<boolean> {
|
|
153
|
+
for (const dep of dependencies) {
|
|
154
|
+
const query = `
|
|
155
|
+
SELECT EXISTS (
|
|
156
|
+
SELECT 1
|
|
157
|
+
FROM information_schema.tables
|
|
158
|
+
WHERE table_schema = 'public'
|
|
159
|
+
AND table_name = $1
|
|
160
|
+
)
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
const result = await this.sourcePool.query(query, [dep]);
|
|
164
|
+
if (!result.rows[0].exists) {
|
|
165
|
+
this.logger.warn(
|
|
166
|
+
`Dependencia ${dep} no encontrada para tabla ${table}`
|
|
167
|
+
);
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|