@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,277 +1,277 @@
|
|
|
1
|
-
import { Logger } from "@nestjs/common";
|
|
2
|
-
import { PrismaClient } from "@prisma/client";
|
|
3
|
-
import { DatabaseConnections } from "./types";
|
|
4
|
-
|
|
5
|
-
export class ForeignKeyManager {
|
|
6
|
-
private readonly logger = new Logger("ForeignKeyManager");
|
|
7
|
-
private readonly dependencyCache: Map<string, any[]> = new Map();
|
|
8
|
-
|
|
9
|
-
constructor(private readonly connections: DatabaseConnections) {}
|
|
10
|
-
|
|
11
|
-
async getForeignKeyDependencies(
|
|
12
|
-
schemaName: string,
|
|
13
|
-
tableName: string
|
|
14
|
-
): Promise<
|
|
15
|
-
Array<{ column: string; foreignTable: string; foreignColumn: string }>
|
|
16
|
-
> {
|
|
17
|
-
const cacheKey = `${schemaName}.${tableName}`;
|
|
18
|
-
if (this.dependencyCache.has(cacheKey)) {
|
|
19
|
-
return this.dependencyCache.get(cacheKey);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
this.logger.log(
|
|
24
|
-
`Obteniendo dependencias de llaves foráneas para ${schemaName}.${tableName}`
|
|
25
|
-
);
|
|
26
|
-
const query = `
|
|
27
|
-
SELECT
|
|
28
|
-
kcu.column_name,
|
|
29
|
-
ccu.table_name AS foreign_table_name,
|
|
30
|
-
ccu.column_name AS foreign_column_name
|
|
31
|
-
FROM
|
|
32
|
-
information_schema.table_constraints tc
|
|
33
|
-
JOIN information_schema.key_column_usage kcu
|
|
34
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
35
|
-
AND tc.table_schema = kcu.table_schema
|
|
36
|
-
JOIN information_schema.constraint_column_usage ccu
|
|
37
|
-
ON ccu.constraint_name = tc.constraint_name
|
|
38
|
-
AND ccu.table_schema = tc.table_schema
|
|
39
|
-
WHERE
|
|
40
|
-
tc.constraint_type = 'FOREIGN KEY'
|
|
41
|
-
AND tc.table_schema = $1
|
|
42
|
-
AND tc.table_name = $2;
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
const { rows } = await this.connections.targetPool.query(query, [
|
|
46
|
-
schemaName,
|
|
47
|
-
tableName,
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
const dependencies = rows.map((row) => ({
|
|
51
|
-
column: row.column_name,
|
|
52
|
-
foreignTable: row.foreign_table_name,
|
|
53
|
-
foreignColumn: row.foreign_column_name,
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
|
-
this.logger.log(
|
|
57
|
-
`Encontradas ${
|
|
58
|
-
dependencies.length
|
|
59
|
-
} dependencias para ${schemaName}.${tableName}:
|
|
60
|
-
${dependencies
|
|
61
|
-
.map(
|
|
62
|
-
(d) => `\n - ${d.column} -> ${d.foreignTable}.${d.foreignColumn}`
|
|
63
|
-
)
|
|
64
|
-
.join("")}`
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
this.dependencyCache.set(cacheKey, dependencies);
|
|
68
|
-
return dependencies;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
this.logger.error(
|
|
71
|
-
`Error obteniendo dependencias de ${schemaName}.${tableName}: ${error.message}`
|
|
72
|
-
);
|
|
73
|
-
return [];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async checkForeignKeyDependencies(
|
|
78
|
-
prisma: PrismaClient,
|
|
79
|
-
tenantId: string,
|
|
80
|
-
record: any,
|
|
81
|
-
dependencies: any[]
|
|
82
|
-
): Promise<string[]> {
|
|
83
|
-
const missingDependencies: string[] = [];
|
|
84
|
-
const recordId = record.id || "unknown";
|
|
85
|
-
|
|
86
|
-
this.logger.debug(
|
|
87
|
-
`Verificando dependencias para registro ${recordId} en tenant ${tenantId}`
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
for (const dep of dependencies) {
|
|
91
|
-
const { table, column, references } = dep;
|
|
92
|
-
const foreignKeyValue = record[column];
|
|
93
|
-
|
|
94
|
-
if (foreignKeyValue === null) {
|
|
95
|
-
this.logger.debug(
|
|
96
|
-
`Dependencia ${table}.${references} es nula para registro ${recordId}, continuando`
|
|
97
|
-
);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Verificar en origen
|
|
102
|
-
const sourceExists = await this.checkSourceDependency(
|
|
103
|
-
table,
|
|
104
|
-
references,
|
|
105
|
-
foreignKeyValue
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
if (!sourceExists) {
|
|
109
|
-
this.logger.debug(
|
|
110
|
-
`Registro ${recordId}: Dependencia ${table}.${references}=${foreignKeyValue} no existe en origen, ignorando`
|
|
111
|
-
);
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Verificar en destino
|
|
116
|
-
const exists = await this.checkDependencyExists(
|
|
117
|
-
prisma,
|
|
118
|
-
tenantId,
|
|
119
|
-
table,
|
|
120
|
-
references,
|
|
121
|
-
foreignKeyValue
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
if (!exists) {
|
|
125
|
-
const depString = `${table}.${references}=${foreignKeyValue}`;
|
|
126
|
-
this.logger.debug(
|
|
127
|
-
`Registro ${recordId}: Dependencia ${depString} existe en origen pero falta en destino`
|
|
128
|
-
);
|
|
129
|
-
missingDependencies.push(depString);
|
|
130
|
-
} else {
|
|
131
|
-
this.logger.debug(
|
|
132
|
-
`Registro ${recordId}: Dependencia ${table}.${references}=${foreignKeyValue} verificada OK`
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (missingDependencies.length > 0) {
|
|
138
|
-
this.logger.debug(
|
|
139
|
-
`Registro ${recordId}: Faltan ${
|
|
140
|
-
missingDependencies.length
|
|
141
|
-
} dependencias:
|
|
142
|
-
${missingDependencies.map((dep) => `\n - ${dep}`).join("")}`
|
|
143
|
-
);
|
|
144
|
-
} else {
|
|
145
|
-
this.logger.debug(`Registro ${recordId}: Todas las dependencias OK`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return missingDependencies;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private async checkSourceDependency(
|
|
152
|
-
table: string,
|
|
153
|
-
column: string,
|
|
154
|
-
value: any
|
|
155
|
-
): Promise<boolean> {
|
|
156
|
-
try {
|
|
157
|
-
const result = await this.connections.sourcePool.query(
|
|
158
|
-
`SELECT 1 FROM "${table}" WHERE "${column}" = $1 LIMIT 1`,
|
|
159
|
-
[value]
|
|
160
|
-
);
|
|
161
|
-
const exists = result.rows.length > 0;
|
|
162
|
-
this.logger.debug(
|
|
163
|
-
`Verificación en origen ${table}.${column}=${value}: ${
|
|
164
|
-
exists ? "EXISTE" : "NO EXISTE"
|
|
165
|
-
}`
|
|
166
|
-
);
|
|
167
|
-
return exists;
|
|
168
|
-
} catch (error) {
|
|
169
|
-
this.logger.error(
|
|
170
|
-
`Error verificando en origen ${table}.${column}=${value}: ${error.message}`
|
|
171
|
-
);
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private async checkDependencyExists(
|
|
177
|
-
prisma: PrismaClient,
|
|
178
|
-
tenantId: string,
|
|
179
|
-
table: string,
|
|
180
|
-
column: string,
|
|
181
|
-
value: any
|
|
182
|
-
): Promise<boolean> {
|
|
183
|
-
try {
|
|
184
|
-
const result = await prisma.$queryRawUnsafe(
|
|
185
|
-
`SELECT 1 FROM "${tenantId}"."${table}" WHERE "${column}" = $1 LIMIT 1`,
|
|
186
|
-
value
|
|
187
|
-
);
|
|
188
|
-
const exists = Array.isArray(result) && result.length > 0;
|
|
189
|
-
this.logger.debug(
|
|
190
|
-
`Verificación en destino ${tenantId}.${table}.${column}=${value}: ${
|
|
191
|
-
exists ? "EXISTE" : "NO EXISTE"
|
|
192
|
-
}`
|
|
193
|
-
);
|
|
194
|
-
return exists;
|
|
195
|
-
} catch (error) {
|
|
196
|
-
this.logger.error(
|
|
197
|
-
`Error verificando en destino ${tenantId}.${table}.${column}=${value}: ${error.message}`
|
|
198
|
-
);
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async verifyDependencies(
|
|
204
|
-
tableName: string,
|
|
205
|
-
record: any,
|
|
206
|
-
tenantId: string
|
|
207
|
-
): Promise<boolean> {
|
|
208
|
-
try {
|
|
209
|
-
const dependencies = await this.getForeignKeyDependencies(
|
|
210
|
-
tableName,
|
|
211
|
-
tableName
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
for (const dep of dependencies) {
|
|
215
|
-
const { foreignTable, foreignColumn } = dep;
|
|
216
|
-
|
|
217
|
-
// Verificar si la tabla existe antes de hacer la consulta
|
|
218
|
-
const tableExists = await this.checkTableExists(foreignTable);
|
|
219
|
-
if (!tableExists) {
|
|
220
|
-
this.logger.warn(
|
|
221
|
-
`Table ${foreignTable} does not exist, skipping dependency check`
|
|
222
|
-
);
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Verificar el valor de la dependencia
|
|
227
|
-
const foreignKeyValue = record[foreignColumn];
|
|
228
|
-
if (foreignKeyValue) {
|
|
229
|
-
const exists = await this.checkDependencyExists(
|
|
230
|
-
this.connections.targetPrisma,
|
|
231
|
-
tenantId,
|
|
232
|
-
foreignTable,
|
|
233
|
-
foreignColumn,
|
|
234
|
-
foreignKeyValue
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
if (!exists) {
|
|
238
|
-
this.logger.warn(
|
|
239
|
-
`Valor ${foreignKeyValue} no existe en ${foreignTable}.${foreignColumn}`
|
|
240
|
-
);
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return true;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
this.logger.error(`Error verifying dependencies: ${error.message}`);
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
private async checkTableExists(tableName: string): Promise<boolean> {
|
|
254
|
-
try {
|
|
255
|
-
const result = await this.connections.sourcePool.query(
|
|
256
|
-
`SELECT EXISTS (
|
|
257
|
-
SELECT FROM information_schema.tables
|
|
258
|
-
WHERE table_schema = 'public'
|
|
259
|
-
AND table_name = $1
|
|
260
|
-
)`,
|
|
261
|
-
[tableName]
|
|
262
|
-
);
|
|
263
|
-
const exists = result.rows[0].exists;
|
|
264
|
-
this.logger.debug(
|
|
265
|
-
`Verificación de existencia de tabla ${tableName}: ${
|
|
266
|
-
exists ? "EXISTE" : "NO EXISTE"
|
|
267
|
-
}`
|
|
268
|
-
);
|
|
269
|
-
return exists;
|
|
270
|
-
} catch (error) {
|
|
271
|
-
this.logger.error(
|
|
272
|
-
`Error verificando existencia de tabla ${tableName}: ${error.message}`
|
|
273
|
-
);
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
import { PrismaClient } from "@prisma/client";
|
|
3
|
+
import { DatabaseConnections } from "./types";
|
|
4
|
+
|
|
5
|
+
export class ForeignKeyManager {
|
|
6
|
+
private readonly logger = new Logger("ForeignKeyManager");
|
|
7
|
+
private readonly dependencyCache: Map<string, any[]> = new Map();
|
|
8
|
+
|
|
9
|
+
constructor(private readonly connections: DatabaseConnections) {}
|
|
10
|
+
|
|
11
|
+
async getForeignKeyDependencies(
|
|
12
|
+
schemaName: string,
|
|
13
|
+
tableName: string
|
|
14
|
+
): Promise<
|
|
15
|
+
Array<{ column: string; foreignTable: string; foreignColumn: string }>
|
|
16
|
+
> {
|
|
17
|
+
const cacheKey = `${schemaName}.${tableName}`;
|
|
18
|
+
if (this.dependencyCache.has(cacheKey)) {
|
|
19
|
+
return this.dependencyCache.get(cacheKey);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
this.logger.log(
|
|
24
|
+
`Obteniendo dependencias de llaves foráneas para ${schemaName}.${tableName}`
|
|
25
|
+
);
|
|
26
|
+
const query = `
|
|
27
|
+
SELECT
|
|
28
|
+
kcu.column_name,
|
|
29
|
+
ccu.table_name AS foreign_table_name,
|
|
30
|
+
ccu.column_name AS foreign_column_name
|
|
31
|
+
FROM
|
|
32
|
+
information_schema.table_constraints tc
|
|
33
|
+
JOIN information_schema.key_column_usage kcu
|
|
34
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
35
|
+
AND tc.table_schema = kcu.table_schema
|
|
36
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
37
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
38
|
+
AND ccu.table_schema = tc.table_schema
|
|
39
|
+
WHERE
|
|
40
|
+
tc.constraint_type = 'FOREIGN KEY'
|
|
41
|
+
AND tc.table_schema = $1
|
|
42
|
+
AND tc.table_name = $2;
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const { rows } = await this.connections.targetPool.query(query, [
|
|
46
|
+
schemaName,
|
|
47
|
+
tableName,
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const dependencies = rows.map((row) => ({
|
|
51
|
+
column: row.column_name,
|
|
52
|
+
foreignTable: row.foreign_table_name,
|
|
53
|
+
foreignColumn: row.foreign_column_name,
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
this.logger.log(
|
|
57
|
+
`Encontradas ${
|
|
58
|
+
dependencies.length
|
|
59
|
+
} dependencias para ${schemaName}.${tableName}:
|
|
60
|
+
${dependencies
|
|
61
|
+
.map(
|
|
62
|
+
(d) => `\n - ${d.column} -> ${d.foreignTable}.${d.foreignColumn}`
|
|
63
|
+
)
|
|
64
|
+
.join("")}`
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
this.dependencyCache.set(cacheKey, dependencies);
|
|
68
|
+
return dependencies;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
this.logger.error(
|
|
71
|
+
`Error obteniendo dependencias de ${schemaName}.${tableName}: ${error.message}`
|
|
72
|
+
);
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async checkForeignKeyDependencies(
|
|
78
|
+
prisma: PrismaClient,
|
|
79
|
+
tenantId: string,
|
|
80
|
+
record: any,
|
|
81
|
+
dependencies: any[]
|
|
82
|
+
): Promise<string[]> {
|
|
83
|
+
const missingDependencies: string[] = [];
|
|
84
|
+
const recordId = record.id || "unknown";
|
|
85
|
+
|
|
86
|
+
this.logger.debug(
|
|
87
|
+
`Verificando dependencias para registro ${recordId} en tenant ${tenantId}`
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
for (const dep of dependencies) {
|
|
91
|
+
const { table, column, references } = dep;
|
|
92
|
+
const foreignKeyValue = record[column];
|
|
93
|
+
|
|
94
|
+
if (foreignKeyValue === null) {
|
|
95
|
+
this.logger.debug(
|
|
96
|
+
`Dependencia ${table}.${references} es nula para registro ${recordId}, continuando`
|
|
97
|
+
);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Verificar en origen
|
|
102
|
+
const sourceExists = await this.checkSourceDependency(
|
|
103
|
+
table,
|
|
104
|
+
references,
|
|
105
|
+
foreignKeyValue
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!sourceExists) {
|
|
109
|
+
this.logger.debug(
|
|
110
|
+
`Registro ${recordId}: Dependencia ${table}.${references}=${foreignKeyValue} no existe en origen, ignorando`
|
|
111
|
+
);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Verificar en destino
|
|
116
|
+
const exists = await this.checkDependencyExists(
|
|
117
|
+
prisma,
|
|
118
|
+
tenantId,
|
|
119
|
+
table,
|
|
120
|
+
references,
|
|
121
|
+
foreignKeyValue
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (!exists) {
|
|
125
|
+
const depString = `${table}.${references}=${foreignKeyValue}`;
|
|
126
|
+
this.logger.debug(
|
|
127
|
+
`Registro ${recordId}: Dependencia ${depString} existe en origen pero falta en destino`
|
|
128
|
+
);
|
|
129
|
+
missingDependencies.push(depString);
|
|
130
|
+
} else {
|
|
131
|
+
this.logger.debug(
|
|
132
|
+
`Registro ${recordId}: Dependencia ${table}.${references}=${foreignKeyValue} verificada OK`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (missingDependencies.length > 0) {
|
|
138
|
+
this.logger.debug(
|
|
139
|
+
`Registro ${recordId}: Faltan ${
|
|
140
|
+
missingDependencies.length
|
|
141
|
+
} dependencias:
|
|
142
|
+
${missingDependencies.map((dep) => `\n - ${dep}`).join("")}`
|
|
143
|
+
);
|
|
144
|
+
} else {
|
|
145
|
+
this.logger.debug(`Registro ${recordId}: Todas las dependencias OK`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return missingDependencies;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private async checkSourceDependency(
|
|
152
|
+
table: string,
|
|
153
|
+
column: string,
|
|
154
|
+
value: any
|
|
155
|
+
): Promise<boolean> {
|
|
156
|
+
try {
|
|
157
|
+
const result = await this.connections.sourcePool.query(
|
|
158
|
+
`SELECT 1 FROM "${table}" WHERE "${column}" = $1 LIMIT 1`,
|
|
159
|
+
[value]
|
|
160
|
+
);
|
|
161
|
+
const exists = result.rows.length > 0;
|
|
162
|
+
this.logger.debug(
|
|
163
|
+
`Verificación en origen ${table}.${column}=${value}: ${
|
|
164
|
+
exists ? "EXISTE" : "NO EXISTE"
|
|
165
|
+
}`
|
|
166
|
+
);
|
|
167
|
+
return exists;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
this.logger.error(
|
|
170
|
+
`Error verificando en origen ${table}.${column}=${value}: ${error.message}`
|
|
171
|
+
);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private async checkDependencyExists(
|
|
177
|
+
prisma: PrismaClient,
|
|
178
|
+
tenantId: string,
|
|
179
|
+
table: string,
|
|
180
|
+
column: string,
|
|
181
|
+
value: any
|
|
182
|
+
): Promise<boolean> {
|
|
183
|
+
try {
|
|
184
|
+
const result = await prisma.$queryRawUnsafe(
|
|
185
|
+
`SELECT 1 FROM "${tenantId}"."${table}" WHERE "${column}" = $1 LIMIT 1`,
|
|
186
|
+
value
|
|
187
|
+
);
|
|
188
|
+
const exists = Array.isArray(result) && result.length > 0;
|
|
189
|
+
this.logger.debug(
|
|
190
|
+
`Verificación en destino ${tenantId}.${table}.${column}=${value}: ${
|
|
191
|
+
exists ? "EXISTE" : "NO EXISTE"
|
|
192
|
+
}`
|
|
193
|
+
);
|
|
194
|
+
return exists;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
this.logger.error(
|
|
197
|
+
`Error verificando en destino ${tenantId}.${table}.${column}=${value}: ${error.message}`
|
|
198
|
+
);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async verifyDependencies(
|
|
204
|
+
tableName: string,
|
|
205
|
+
record: any,
|
|
206
|
+
tenantId: string
|
|
207
|
+
): Promise<boolean> {
|
|
208
|
+
try {
|
|
209
|
+
const dependencies = await this.getForeignKeyDependencies(
|
|
210
|
+
tableName,
|
|
211
|
+
tableName
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
for (const dep of dependencies) {
|
|
215
|
+
const { foreignTable, foreignColumn } = dep;
|
|
216
|
+
|
|
217
|
+
// Verificar si la tabla existe antes de hacer la consulta
|
|
218
|
+
const tableExists = await this.checkTableExists(foreignTable);
|
|
219
|
+
if (!tableExists) {
|
|
220
|
+
this.logger.warn(
|
|
221
|
+
`Table ${foreignTable} does not exist, skipping dependency check`
|
|
222
|
+
);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Verificar el valor de la dependencia
|
|
227
|
+
const foreignKeyValue = record[foreignColumn];
|
|
228
|
+
if (foreignKeyValue) {
|
|
229
|
+
const exists = await this.checkDependencyExists(
|
|
230
|
+
this.connections.targetPrisma,
|
|
231
|
+
tenantId,
|
|
232
|
+
foreignTable,
|
|
233
|
+
foreignColumn,
|
|
234
|
+
foreignKeyValue
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
if (!exists) {
|
|
238
|
+
this.logger.warn(
|
|
239
|
+
`Valor ${foreignKeyValue} no existe en ${foreignTable}.${foreignColumn}`
|
|
240
|
+
);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return true;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
this.logger.error(`Error verifying dependencies: ${error.message}`);
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private async checkTableExists(tableName: string): Promise<boolean> {
|
|
254
|
+
try {
|
|
255
|
+
const result = await this.connections.sourcePool.query(
|
|
256
|
+
`SELECT EXISTS (
|
|
257
|
+
SELECT FROM information_schema.tables
|
|
258
|
+
WHERE table_schema = 'public'
|
|
259
|
+
AND table_name = $1
|
|
260
|
+
)`,
|
|
261
|
+
[tableName]
|
|
262
|
+
);
|
|
263
|
+
const exists = result.rows[0].exists;
|
|
264
|
+
this.logger.debug(
|
|
265
|
+
`Verificación de existencia de tabla ${tableName}: ${
|
|
266
|
+
exists ? "EXISTE" : "NO EXISTE"
|
|
267
|
+
}`
|
|
268
|
+
);
|
|
269
|
+
return exists;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
this.logger.error(
|
|
272
|
+
`Error verificando existencia de tabla ${tableName}: ${error.message}`
|
|
273
|
+
);
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
{
|
|
2
|
-
"commonSchema": "public",
|
|
3
|
-
"tenantInfo": {
|
|
4
|
-
"sourceTable": "users",
|
|
5
|
-
"tenantIdColumn": "user_id",
|
|
6
|
-
"providerIdColumn": "provider_id"
|
|
7
|
-
},
|
|
8
|
-
"migrationPriorities": {
|
|
9
|
-
"high": ["countries", "providers", "users", "api_keys"],
|
|
10
|
-
"medium": ["transactions", "charges", "balances"],
|
|
11
|
-
"low": ["invoices", "api_request_logs"]
|
|
12
|
-
},
|
|
13
|
-
"tables": {
|
|
14
|
-
"countries": {
|
|
15
|
-
"type": "public",
|
|
16
|
-
"idField": "id"
|
|
17
|
-
},
|
|
18
|
-
"providers": {
|
|
19
|
-
"type": "public",
|
|
20
|
-
"idField": "provider_id",
|
|
21
|
-
"dependencies": ["countries"]
|
|
22
|
-
},
|
|
23
|
-
"users": {
|
|
24
|
-
"type": "public",
|
|
25
|
-
"idField": "user_id",
|
|
26
|
-
"dependencies": ["providers"]
|
|
27
|
-
},
|
|
28
|
-
"api_keys": {
|
|
29
|
-
"type": "public",
|
|
30
|
-
"idField": "id",
|
|
31
|
-
"dependencies": ["users"]
|
|
32
|
-
},
|
|
33
|
-
"transactions": {
|
|
34
|
-
"type": "filteredPublic",
|
|
35
|
-
"idField": "id",
|
|
36
|
-
"filterColumn": "provider_id",
|
|
37
|
-
"dependencies": ["users", "providers"]
|
|
38
|
-
},
|
|
39
|
-
"charges": {
|
|
40
|
-
"type": "filteredPublic",
|
|
41
|
-
"idField": "id",
|
|
42
|
-
"filterColumn": "provider_id",
|
|
43
|
-
"dependencies": ["transactions"]
|
|
44
|
-
},
|
|
45
|
-
"invoices": {
|
|
46
|
-
"type": "filteredPublic",
|
|
47
|
-
"idField": "id",
|
|
48
|
-
"filterColumn": "provider_id",
|
|
49
|
-
"dependencies": ["transactions", "charges"]
|
|
50
|
-
},
|
|
51
|
-
"balances": {
|
|
52
|
-
"type": "filteredPublic",
|
|
53
|
-
"idField": "id",
|
|
54
|
-
"filterColumn": "provider_id",
|
|
55
|
-
"dependencies": ["users"]
|
|
56
|
-
},
|
|
57
|
-
"api_request_logs": {
|
|
58
|
-
"type": "filteredPublic",
|
|
59
|
-
"idField": "id",
|
|
60
|
-
"dependencies": ["api_keys"]
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"commonSchema": "public",
|
|
3
|
+
"tenantInfo": {
|
|
4
|
+
"sourceTable": "users",
|
|
5
|
+
"tenantIdColumn": "user_id",
|
|
6
|
+
"providerIdColumn": "provider_id"
|
|
7
|
+
},
|
|
8
|
+
"migrationPriorities": {
|
|
9
|
+
"high": ["countries", "providers", "users", "api_keys"],
|
|
10
|
+
"medium": ["transactions", "charges", "balances"],
|
|
11
|
+
"low": ["invoices", "api_request_logs"]
|
|
12
|
+
},
|
|
13
|
+
"tables": {
|
|
14
|
+
"countries": {
|
|
15
|
+
"type": "public",
|
|
16
|
+
"idField": "id"
|
|
17
|
+
},
|
|
18
|
+
"providers": {
|
|
19
|
+
"type": "public",
|
|
20
|
+
"idField": "provider_id",
|
|
21
|
+
"dependencies": ["countries"]
|
|
22
|
+
},
|
|
23
|
+
"users": {
|
|
24
|
+
"type": "public",
|
|
25
|
+
"idField": "user_id",
|
|
26
|
+
"dependencies": ["providers"]
|
|
27
|
+
},
|
|
28
|
+
"api_keys": {
|
|
29
|
+
"type": "public",
|
|
30
|
+
"idField": "id",
|
|
31
|
+
"dependencies": ["users"]
|
|
32
|
+
},
|
|
33
|
+
"transactions": {
|
|
34
|
+
"type": "filteredPublic",
|
|
35
|
+
"idField": "id",
|
|
36
|
+
"filterColumn": "provider_id",
|
|
37
|
+
"dependencies": ["users", "providers"]
|
|
38
|
+
},
|
|
39
|
+
"charges": {
|
|
40
|
+
"type": "filteredPublic",
|
|
41
|
+
"idField": "id",
|
|
42
|
+
"filterColumn": "provider_id",
|
|
43
|
+
"dependencies": ["transactions"]
|
|
44
|
+
},
|
|
45
|
+
"invoices": {
|
|
46
|
+
"type": "filteredPublic",
|
|
47
|
+
"idField": "id",
|
|
48
|
+
"filterColumn": "provider_id",
|
|
49
|
+
"dependencies": ["transactions", "charges"]
|
|
50
|
+
},
|
|
51
|
+
"balances": {
|
|
52
|
+
"type": "filteredPublic",
|
|
53
|
+
"idField": "id",
|
|
54
|
+
"filterColumn": "provider_id",
|
|
55
|
+
"dependencies": ["users"]
|
|
56
|
+
},
|
|
57
|
+
"api_request_logs": {
|
|
58
|
+
"type": "filteredPublic",
|
|
59
|
+
"idField": "id",
|
|
60
|
+
"dependencies": ["api_keys"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|