@javalabs/prisma-client 1.0.4 → 1.0.8

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.
Files changed (75) hide show
  1. package/dist/scripts/data-migration/batch-migrator.d.ts +14 -19
  2. package/dist/scripts/data-migration/batch-migrator.js +98 -297
  3. package/dist/scripts/data-migration/batch-migrator.js.map +1 -1
  4. package/dist/scripts/data-migration/data-transformer.d.ts +16 -7
  5. package/dist/scripts/data-migration/data-transformer.js +169 -133
  6. package/dist/scripts/data-migration/data-transformer.js.map +1 -1
  7. package/dist/scripts/data-migration/db-connector.d.ts +6 -1
  8. package/dist/scripts/data-migration/db-connector.js +44 -8
  9. package/dist/scripts/data-migration/db-connector.js.map +1 -1
  10. package/dist/scripts/data-migration/dependency-resolver.d.ts +10 -10
  11. package/dist/scripts/data-migration/dependency-resolver.js +92 -211
  12. package/dist/scripts/data-migration/dependency-resolver.js.map +1 -1
  13. package/dist/scripts/data-migration/foreign-key-manager.d.ts +6 -5
  14. package/dist/scripts/data-migration/foreign-key-manager.js +108 -18
  15. package/dist/scripts/data-migration/foreign-key-manager.js.map +1 -1
  16. package/dist/scripts/data-migration/migration-config.json +63 -0
  17. package/dist/scripts/data-migration/migration-tool.d.ts +25 -6
  18. package/dist/scripts/data-migration/migration-tool.js +78 -38
  19. package/dist/scripts/data-migration/migration-tool.js.map +1 -1
  20. package/dist/scripts/data-migration/multi-source-migrator.d.ts +17 -0
  21. package/dist/scripts/data-migration/multi-source-migrator.js +130 -0
  22. package/dist/scripts/data-migration/multi-source-migrator.js.map +1 -0
  23. package/dist/scripts/data-migration/schema-utils.d.ts +3 -3
  24. package/dist/scripts/data-migration/schema-utils.js +62 -19
  25. package/dist/scripts/data-migration/schema-utils.js.map +1 -1
  26. package/dist/scripts/data-migration/tenant-migrator.js +9 -2
  27. package/dist/scripts/data-migration/tenant-migrator.js.map +1 -1
  28. package/dist/scripts/data-migration/typecast-manager.d.ts +7 -3
  29. package/dist/scripts/data-migration/typecast-manager.js +169 -25
  30. package/dist/scripts/data-migration/typecast-manager.js.map +1 -1
  31. package/dist/scripts/data-migration/types.d.ts +68 -2
  32. package/dist/scripts/database-initializer.d.ts +5 -0
  33. package/dist/scripts/database-initializer.js +45 -0
  34. package/dist/scripts/database-initializer.js.map +1 -0
  35. package/dist/scripts/fix-table-indexes.d.ts +26 -0
  36. package/dist/scripts/fix-table-indexes.js +460 -0
  37. package/dist/scripts/fix-table-indexes.js.map +1 -0
  38. package/dist/scripts/multi-db-migration.d.ts +1 -0
  39. package/dist/scripts/multi-db-migration.js +55 -0
  40. package/dist/scripts/multi-db-migration.js.map +1 -0
  41. package/dist/scripts/post-migration-validator.d.ts +18 -5
  42. package/dist/scripts/post-migration-validator.js +61 -39
  43. package/dist/scripts/post-migration-validator.js.map +1 -1
  44. package/dist/scripts/run-migration.js +83 -96
  45. package/dist/scripts/run-migration.js.map +1 -1
  46. package/dist/scripts/sequence-synchronizer.d.ts +8 -0
  47. package/dist/scripts/sequence-synchronizer.js +88 -0
  48. package/dist/scripts/sequence-synchronizer.js.map +1 -0
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/migration-config.json +40 -72
  51. package/{migration-config-public.json → migration-config.json.bk} +14 -14
  52. package/package.json +10 -3
  53. package/prisma/migrations/{20250422001248_add_bank_account_id → 0_init}/migration.sql +269 -47
  54. package/prisma/migrations/20250505171324_add_bank_owner_name_in_manual_transfer/migration.sql +2 -0
  55. package/prisma/migrations/20250505173850_add_method_name_in_manual_transfer/migration.sql +2 -0
  56. package/prisma/migrations/migration_lock.toml +2 -2
  57. package/prisma/schema.prisma +214 -77
  58. package/src/scripts/data-migration/batch-migrator.ts +192 -513
  59. package/src/scripts/data-migration/data-transformer.ts +252 -203
  60. package/src/scripts/data-migration/db-connector.ts +66 -13
  61. package/src/scripts/data-migration/dependency-resolver.ts +121 -266
  62. package/src/scripts/data-migration/foreign-key-manager.ts +214 -32
  63. package/src/scripts/data-migration/migration-config.json +63 -0
  64. package/src/scripts/data-migration/migration-tool.ts +377 -225
  65. package/src/scripts/data-migration/schema-utils.ts +94 -32
  66. package/src/scripts/data-migration/tenant-migrator.ts +12 -5
  67. package/src/scripts/data-migration/typecast-manager.ts +186 -31
  68. package/src/scripts/data-migration/types.ts +78 -5
  69. package/src/scripts/database-initializer.ts +49 -0
  70. package/src/scripts/fix-table-indexes.ts +602 -0
  71. package/src/scripts/post-migration-validator.ts +206 -107
  72. package/src/scripts/run-migration.ts +140 -124
  73. package/src/scripts/sequence-synchronizer.ts +127 -0
  74. package/prisma/migrations/20250422001957_add_bank_account_id_relations/migration.sql +0 -14
  75. package/src/scripts/dumps/source_dump_20250413_112626.sql +0 -1527
@@ -1,319 +1,174 @@
1
1
  import { Logger } from "@nestjs/common";
2
2
  import { Pool } from "pg";
3
3
 
4
- interface TableDependency {
5
- table: string;
6
- dependencies: string[];
7
- level?: number;
8
- }
9
-
10
4
  export class DependencyResolver {
11
5
  private readonly logger = new Logger("DependencyResolver");
12
- private dependencyGraph: Map<string, TableDependency> = new Map();
6
+ private readonly visitedTables = new Set<string>();
7
+ private readonly temporaryMark = new Set<string>();
8
+ private readonly tableOrder: string[] = [];
9
+ private config: any;
13
10
 
14
11
  constructor(
15
12
  private readonly sourcePool: Pool,
16
13
  private readonly targetPool: Pool
17
14
  ) {}
18
15
 
16
+ setConfig(config: any) {
17
+ this.config = config;
18
+ }
19
+
19
20
  async analyzeDependencies(): Promise<string[]> {
20
21
  try {
21
- // Get all tables
22
- const tables = await this.getTables();
22
+ this.logger.log("Iniciando análisis de dependencias...");
23
23
 
24
- // Build dependency graph
25
- for (const table of tables) {
26
- const foreignKeys = await this.getForeignKeys(table);
24
+ // Obtener todas las tablas del esquema
25
+ const tables = await this.getAllTables();
27
26
 
28
- // Add manual dependencies for specific cases
29
- const manualDependencies = this.getManualDependencies(table);
30
- const allDependencies = [
31
- ...new Set([...foreignKeys, ...manualDependencies]),
32
- ];
27
+ // Resetear estado
28
+ this.visitedTables.clear();
29
+ this.temporaryMark.clear();
30
+ this.tableOrder.length = 0;
33
31
 
34
- this.dependencyGraph.set(table, {
35
- table,
36
- dependencies: allDependencies,
37
- });
38
- }
32
+ // Ordenar tablas por prioridad primero
33
+ const priorityOrder = this.getPriorityOrder(tables);
39
34
 
40
- // Assign levels to tables based on dependencies
41
- this.assignLevels();
42
-
43
- // Sort tables by dependency level
44
- const sortedTables = Array.from(this.dependencyGraph.values())
45
- .sort((a, b) => (a.level || 0) - (b.level || 0))
46
- .map((dep) => dep.table);
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
+ }
47
43
 
48
- this.logger.log(`Migration order: ${sortedTables.join(" -> ")}`);
49
- return sortedTables;
44
+ this.logger.log(
45
+ `Orden de migración determinado: ${this.tableOrder.join(" -> ")}`
46
+ );
47
+ return this.tableOrder;
50
48
  } catch (error) {
51
- this.logger.error(`Error analyzing dependencies: ${error.message}`);
49
+ this.logger.error(`Error analizando dependencias: ${error.message}`);
52
50
  throw error;
53
51
  }
54
52
  }
55
53
 
56
- private getManualDependencies(tableName: string): string[] {
57
- // Define manual dependencies for specific cases
58
- const manualDependencyMap: Record<string, string[]> = {
59
- api_keys: ["users", "providers"],
60
- balances: ["users"],
61
- customers: ["users"],
62
- global_user_transactions: ["users", "providers", "transactions"],
63
- invoices: ["transactions"],
64
- credit_requests: ["users", "invoices"],
65
- notifications: ["users", "transactions"],
66
- pending_references: ["users", "providers"],
67
- daily_logs: ["transactions"],
68
- transaction_updates: ["transactions"],
69
- toku: ["transactions"],
70
- providers: ["countries"],
71
- users: ["countries"],
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 || []),
72
59
  };
73
60
 
74
- return manualDependencyMap[tableName] || [];
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];
75
73
  }
76
74
 
77
- private async getTables(): Promise<string[]> {
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[]> {
78
105
  const query = `
79
106
  SELECT table_name
80
107
  FROM information_schema.tables
81
108
  WHERE table_schema = 'public'
82
109
  AND table_type = 'BASE TABLE'
83
- AND table_name NOT IN ('_prisma_migrations', 'schema_migrations')
84
110
  `;
85
111
 
86
112
  const result = await this.sourcePool.query(query);
87
113
  return result.rows.map((row) => row.table_name);
88
114
  }
89
115
 
90
- private async getForeignKeys(tableName: string): Promise<string[]> {
91
- try {
92
- // Get explicit foreign keys (mantener el query existente)
93
- const foreignKeyQuery = `
94
- SELECT DISTINCT
95
- ccu.table_name AS foreign_table_name,
96
- kcu.column_name AS column_name
97
- FROM
98
- information_schema.table_constraints AS tc
99
- JOIN information_schema.constraint_column_usage AS ccu
100
- ON ccu.constraint_name = tc.constraint_name
101
- JOIN information_schema.key_column_usage AS kcu
102
- ON kcu.constraint_name = tc.constraint_name
103
- WHERE
104
- tc.constraint_type = 'FOREIGN KEY'
105
- AND tc.table_name = $1
106
- AND tc.table_schema = 'public'
107
- `;
108
-
109
- // Expanded implicit dependencies patterns
110
- const implicitDependencyQuery = `
111
- SELECT DISTINCT
112
- c.column_name,
113
- c.udt_name,
114
- c.column_default,
115
- c.data_type
116
- FROM
117
- information_schema.columns c
118
- WHERE
119
- c.table_schema = 'public'
120
- AND c.table_name = $1
121
- AND (
122
- -- Common ID patterns
123
- c.column_name LIKE '%_id'
124
- OR c.column_name LIKE '%_ids'
125
- OR c.column_name LIKE 'id_%'
126
- -- Reference patterns
127
- OR c.column_name LIKE '%_ref%'
128
- OR c.column_name LIKE '%_key'
129
- OR c.column_name LIKE '%_code'
130
- -- Relationship patterns
131
- OR c.column_name LIKE '%_by'
132
- OR c.column_name LIKE '%_to'
133
- OR c.column_name LIKE '%_from'
134
- OR c.column_name LIKE 'parent_%'
135
- OR c.column_name LIKE 'child_%'
136
- -- JSON/JSONB columns that might contain references
137
- OR (c.data_type IN ('json', 'jsonb') AND c.column_name LIKE '%_data')
138
- -- Array columns that might contain IDs
139
- OR (c.data_type = 'ARRAY' AND c.column_name LIKE '%_ids')
140
- -- Enum columns that might indicate relationships
141
- OR (c.udt_name LIKE 'enum_%' AND c.column_name LIKE '%_type')
142
- )
143
- `;
144
-
145
- // Get referenced tables through column comments or descriptions
146
- const referencedTablesQuery = `
147
- SELECT DISTINCT
148
- obj_description(c.table_name::regclass, 'pg_class') as table_comment
149
- FROM
150
- information_schema.columns c
151
- WHERE
152
- c.table_schema = 'public'
153
- AND c.table_name = $1
154
- `;
116
+ private async getTableDependencies(table: string): Promise<string[]> {
117
+ const dependencies = new Set<string>();
155
118
 
156
- const [fkResult, implicitResult, referencedResult] = await Promise.all([
157
- this.sourcePool.query(foreignKeyQuery, [tableName]),
158
- this.sourcePool.query(implicitDependencyQuery, [tableName]),
159
- this.sourcePool.query(referencedTablesQuery, [tableName]),
160
- ]);
119
+ // Obtener dependencias de la configuración
120
+ const configDeps = this.config?.tables?.[table]?.dependencies || [];
121
+ configDeps.forEach((dep) => dependencies.add(dep));
161
122
 
162
- const allDependencies = new Set<string>();
163
-
164
- // Add explicit foreign key dependencies
165
- fkResult.rows.forEach((row) =>
166
- allDependencies.add(row.foreign_table_name)
167
- );
168
-
169
- // Add implicit dependencies
170
- implicitResult.rows.forEach((row) => {
171
- const columnName = row.column_name;
172
- // Convert column name to potential table name (e.g., user_id -> users)
173
- const potentialTable = this.inferTableNameFromColumn(columnName);
174
- if (potentialTable) {
175
- allDependencies.add(potentialTable);
176
- }
177
- });
178
-
179
- // Parse table comments for additional dependencies
180
- referencedResult.rows.forEach((row) => {
181
- if (row.table_comment) {
182
- const referencedTables = this.parseTableComment(row.table_comment);
183
- referencedTables.forEach((table) => allDependencies.add(table));
184
- }
185
- });
186
-
187
- // Añadir lógica adicional para procesar los resultados
188
- implicitResult.rows.forEach((row) => {
189
- // Procesar columnas JSON/JSONB
190
- if (row.data_type === "json" || row.data_type === "jsonb") {
191
- const potentialRefs = this.extractJsonReferences(row.column_name);
192
- potentialRefs.forEach((ref) => allDependencies.add(ref));
193
- }
194
-
195
- // Procesar columnas de tipo enum
196
- if (row.udt_name?.startsWith("enum_")) {
197
- const enumTable = this.inferTableFromEnum(row.udt_name);
198
- if (enumTable) allDependencies.add(enumTable);
199
- }
200
-
201
- // Procesar arrays
202
- if (row.data_type === "ARRAY") {
203
- const arrayTable = this.inferTableFromArrayColumn(row.column_name);
204
- if (arrayTable) allDependencies.add(arrayTable);
205
- }
206
- });
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
+ `;
207
136
 
208
- return Array.from(allDependencies);
137
+ try {
138
+ const result = await this.sourcePool.query(query, [table]);
139
+ result.rows.forEach((row) => dependencies.add(row.foreign_table));
209
140
  } catch (error) {
210
- this.logger.error(
211
- `Error getting dependencies for ${tableName}: ${error.message}`
141
+ this.logger.warn(
142
+ `Error obteniendo dependencias para ${table}: ${error.message}`
212
143
  );
213
- return [];
214
- }
215
- }
216
-
217
- private inferTableFromEnum(enumType: string): string | null {
218
- // enum_transaction_status -> transactions
219
- const parts = enumType.split("_");
220
- if (parts.length > 1) {
221
- return parts[1];
222
- }
223
- return null;
224
- }
225
-
226
- private inferTableFromArrayColumn(columnName: string): string | null {
227
- // user_ids -> users
228
- if (columnName.endsWith("_ids")) {
229
- return columnName.slice(0, -4) + "s";
230
144
  }
231
- return null;
232
- }
233
145
 
234
- private extractJsonReferences(columnName: string): string[] {
235
- // metadata_data -> buscar referencias en el nombre
236
- const refs: string[] = [];
237
- const parts = columnName.split("_");
238
- if (parts.length > 1) {
239
- const potentialTable = parts[0] + "s";
240
- refs.push(potentialTable);
241
- }
242
- return refs;
146
+ return Array.from(dependencies);
243
147
  }
244
148
 
245
- private inferTableNameFromColumn(columnName: string): string | null {
246
- // Remove common suffixes
247
- const suffixes = ["_id", "_by", "_ref", "_fk"];
248
- let tableName = columnName;
249
-
250
- for (const suffix of suffixes) {
251
- if (tableName.endsWith(suffix)) {
252
- tableName = tableName.slice(0, -suffix.length);
253
- break;
254
- }
255
- }
256
-
257
- // Convert to plural form (simple conversion)
258
- if (!tableName.endsWith("s")) {
259
- tableName += "s";
260
- }
261
-
262
- return tableName;
263
- }
264
-
265
- private parseTableComment(comment: string): string[] {
266
- // Extract table references from comments
267
- // Example comment format: "References: users, transactions"
268
- const referencesMatch = comment.match(/References:\s*([^;]+)/i);
269
- if (referencesMatch) {
270
- return referencesMatch[1]
271
- .split(",")
272
- .map((table) => table.trim())
273
- .filter((table) => table.length > 0);
274
- }
275
- return [];
276
- }
277
-
278
- private assignLevels(): void {
279
- const visited = new Set<string>();
280
- const visiting = new Set<string>();
281
-
282
- const visit = (tableName: string, level = 0): number => {
283
- if (visiting.has(tableName)) {
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) {
284
165
  this.logger.warn(
285
- `Circular dependency detected involving table: ${tableName}`
166
+ `Dependencia ${dep} no encontrada para tabla ${table}`
286
167
  );
287
- return level;
288
- }
289
-
290
- if (visited.has(tableName)) {
291
- return this.dependencyGraph.get(tableName)?.level || 0;
292
- }
293
-
294
- visiting.add(tableName);
295
-
296
- const node = this.dependencyGraph.get(tableName);
297
- if (!node) return level;
298
-
299
- let maxDepLevel = level;
300
- for (const dep of node.dependencies) {
301
- const depLevel = visit(dep, level + 1);
302
- maxDepLevel = Math.max(maxDepLevel, depLevel + 1);
303
- }
304
-
305
- node.level = maxDepLevel;
306
- visited.add(tableName);
307
- visiting.delete(tableName);
308
-
309
- return maxDepLevel;
310
- };
311
-
312
- // Start with all tables
313
- for (const tableName of this.dependencyGraph.keys()) {
314
- if (!visited.has(tableName)) {
315
- visit(tableName);
168
+ return false;
316
169
  }
317
170
  }
171
+
172
+ return true;
318
173
  }
319
174
  }