@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
@@ -4,7 +4,7 @@ import { ColumnSchema } from "./types";
4
4
  export class SchemaUtils {
5
5
  private readonly logger = new Logger("SchemaUtils");
6
6
 
7
- constructor(private readonly sourcePool: any, private readonly targetPool: any) {}
7
+ constructor(private readonly connections: any) {}
8
8
 
9
9
  // En el método getTableSchema de SchemaUtils
10
10
  async getTableSchema(
@@ -12,8 +12,11 @@ export class SchemaUtils {
12
12
  dbType: "source" | "target",
13
13
  schema: string = "public"
14
14
  ): Promise<ColumnSchema[]> {
15
- const pool = dbType === "source" ? this.sourcePool : this.targetPool;
16
-
15
+ const pool =
16
+ dbType === "source"
17
+ ? this.connections.sourcePool
18
+ : this.connections.targetPool;
19
+
17
20
  const query = `
18
21
  SELECT
19
22
  c.table_name,
@@ -37,13 +40,15 @@ export class SchemaUtils {
37
40
  ORDER BY
38
41
  c.ordinal_position
39
42
  `;
40
-
43
+
41
44
  try {
42
45
  const result = await pool.query(query, [schema, tableName]);
43
46
  // No transformar los nombres de las columnas, mantener el case original
44
47
  return result.rows;
45
48
  } catch (error) {
46
- this.logger.error(`Error getting schema for ${schema}.${tableName}: ${error.message}`);
49
+ this.logger.error(
50
+ `Error getting schema for ${schema}.${tableName}: ${error.message}`
51
+ );
47
52
  return [];
48
53
  }
49
54
  }
@@ -55,7 +60,9 @@ export class SchemaUtils {
55
60
  FROM information_schema.schemata
56
61
  WHERE schema_name = $1
57
62
  `;
58
- const result = await this.targetPool.query(query, [schemaName]);
63
+ const result = await this.connections.targetPool.query(query, [
64
+ schemaName,
65
+ ]);
59
66
  return result.rows.length > 0;
60
67
  } catch (error) {
61
68
  this.logger.error(`Error checking if schema exists: ${error.message}`);
@@ -71,14 +78,18 @@ export class SchemaUtils {
71
78
  WHERE table_schema = $1
72
79
  AND table_type = 'BASE TABLE'
73
80
  `;
74
- const result = await this.targetPool.query(query, [schemaName]);
75
-
81
+ const result = await this.connections.targetPool.query(query, [
82
+ schemaName,
83
+ ]);
84
+
76
85
  // Log para depuración
77
- this.logger.log(`Found ${result.rows.length} tables in schema ${schemaName}`);
78
- result.rows.forEach(row => {
86
+ this.logger.log(
87
+ `Found ${result.rows.length} tables in schema ${schemaName}`
88
+ );
89
+ result.rows.forEach((row) => {
79
90
  this.logger.log(`Table in schema ${schemaName}: ${row.table_name}`);
80
91
  });
81
-
92
+
82
93
  return result.rows.map((row) => row.table_name);
83
94
  } catch (error) {
84
95
  this.logger.error(`Error getting target tables: ${error.message}`);
@@ -86,10 +97,7 @@ export class SchemaUtils {
86
97
  }
87
98
  }
88
99
 
89
- async getEnumValues(
90
- schemaName: string,
91
- enumName: string
92
- ): Promise<string[]> {
100
+ async getEnumValues(schemaName: string, enumName: string): Promise<string[]> {
93
101
  try {
94
102
  // Consulta para obtener los valores posibles de un enum
95
103
  const query = `
@@ -100,7 +108,10 @@ export class SchemaUtils {
100
108
  WHERE t.typname = $1
101
109
  AND n.nspname = $2
102
110
  `;
103
- const result = await this.targetPool.query(query, [enumName, schemaName]);
111
+ const result = await this.connections.targetPool.query(query, [
112
+ enumName,
113
+ schemaName,
114
+ ]);
104
115
  return result.rows.map((row) => row.enumlabel);
105
116
  } catch (error) {
106
117
  this.logger.error(
@@ -111,26 +122,30 @@ export class SchemaUtils {
111
122
  }
112
123
 
113
124
  async executeQuery(query: string, params: any[] = []): Promise<any> {
114
- return this.targetPool.query(query, params);
125
+ return this.connections.targetPool.query(query, params);
115
126
  }
116
127
 
117
128
  async queryTargetDb(query: string, params: any[]): Promise<{ rows: any[] }> {
118
- return this.targetPool.query(query, params);
129
+ return this.connections.targetPool.query(query, params);
119
130
  }
120
131
 
121
- async createMissingColumns(tableName: string, schema: string, columns: ColumnSchema[]) {
132
+ async createMissingColumns(
133
+ tableName: string,
134
+ schema: string,
135
+ columns: ColumnSchema[]
136
+ ) {
122
137
  for (const column of columns) {
123
138
  try {
124
139
  const query = `
125
140
  ALTER TABLE "${schema}"."${tableName}"
126
141
  ADD COLUMN IF NOT EXISTS "${column.column_name}" ${column.data_type}
127
- ${column.is_nullable === 'NO' ? 'NOT NULL' : ''}
128
- ${column.column_default ? `DEFAULT ${column.column_default}` : ''}
142
+ ${column.is_nullable === "NO" ? "NOT NULL" : ""}
143
+ ${column.column_default ? `DEFAULT ${column.column_default}` : ""}
129
144
  `;
130
- await this.targetPool.query(query);
145
+ await this.connections.targetPool.query(query);
131
146
 
132
147
  // Add unique constraint if needed
133
- if (column.constraint_type === 'UNIQUE') {
148
+ if (column.constraint_type === "UNIQUE") {
134
149
  const constraintName = `${tableName}_${column.column_name}_unique`;
135
150
  const uniqueQuery = `
136
151
  DO $$
@@ -143,10 +158,12 @@ export class SchemaUtils {
143
158
  END IF;
144
159
  END $$;
145
160
  `;
146
- await this.targetPool.query(uniqueQuery);
161
+ await this.connections.targetPool.query(uniqueQuery);
147
162
  }
148
163
  } catch (error) {
149
- this.logger.error(`Error creating column ${column.column_name}: ${error.message}`);
164
+ this.logger.error(
165
+ `Error creating column ${column.column_name}: ${error.message}`
166
+ );
150
167
  throw error;
151
168
  }
152
169
  }
@@ -155,13 +172,15 @@ export class SchemaUtils {
155
172
  async createSchema(schemaName: string): Promise<void> {
156
173
  try {
157
174
  // Create schema
158
- await this.targetPool.query(`CREATE SCHEMA IF NOT EXISTS "${schemaName}"`);
175
+ await this.connections.targetPool.query(
176
+ `CREATE SCHEMA IF NOT EXISTS "${schemaName}"`
177
+ );
159
178
 
160
179
  // Copy table structure from public schema
161
180
  const tables = await this.getPublicTables();
162
-
181
+
163
182
  for (const table of tables) {
164
- await this.targetPool.query(`
183
+ await this.connections.targetPool.query(`
165
184
  CREATE TABLE IF NOT EXISTS "${schemaName}"."${table}" (
166
185
  LIKE public."${table}" INCLUDING ALL
167
186
  );
@@ -170,17 +189,60 @@ export class SchemaUtils {
170
189
 
171
190
  this.logger.log(`Created schema ${schemaName} with all tables`);
172
191
  } catch (error) {
173
- this.logger.error(`Error creating schema ${schemaName}: ${error.message}`);
192
+ this.logger.error(
193
+ `Error creating schema ${schemaName}: ${error.message}`
194
+ );
174
195
  throw error;
175
196
  }
176
197
  }
177
198
 
178
199
  private async getPublicTables(): Promise<string[]> {
179
- const result = await this.targetPool.query(`
200
+ const result = await this.connections.targetPool.query(`
180
201
  SELECT tablename
181
202
  FROM pg_tables
182
203
  WHERE schemaname = 'public'
183
204
  `);
184
- return result.rows.map(row => row.tablename);
205
+ return result.rows.map((row) => row.tablename);
206
+ }
207
+
208
+ async getTableColumns(
209
+ schema: string,
210
+ table: string
211
+ ): Promise<ColumnSchema[]> {
212
+ const query = `
213
+ SELECT
214
+ column_name,
215
+ data_type,
216
+ is_nullable,
217
+ column_default,
218
+ character_maximum_length,
219
+ numeric_precision,
220
+ numeric_scale
221
+ FROM information_schema.columns
222
+ WHERE table_schema = $1
223
+ AND table_name = $2
224
+ ORDER BY ordinal_position;
225
+ `;
226
+
227
+ try {
228
+ const result = await this.connections.sourcePool.query(query, [
229
+ schema,
230
+ table,
231
+ ]);
232
+ return result.rows.map((row: any) => ({
233
+ column_name: row.column_name,
234
+ data_type: row.data_type,
235
+ is_nullable: row.is_nullable,
236
+ column_default: row.column_default,
237
+ character_maximum_length: row.character_maximum_length,
238
+ numeric_precision: row.numeric_precision,
239
+ numeric_scale: row.numeric_scale,
240
+ }));
241
+ } catch (error) {
242
+ this.logger.error(
243
+ `Error obteniendo columnas para ${schema}.${table}: ${error.message}`
244
+ );
245
+ throw error;
246
+ }
185
247
  }
186
- }
248
+ }
@@ -162,7 +162,7 @@ export class TenantMigrator {
162
162
  const bIndex = sortedTables.indexOf(b.name);
163
163
  return aIndex - bIndex;
164
164
  })
165
- .filter(entity => sortedTables.includes(entity.name));
165
+ .filter((entity) => sortedTables.includes(entity.name));
166
166
 
167
167
  this.logger.log(
168
168
  `Migration order: ${orderedEntities.map((e) => e.name).join(" -> ")}`
@@ -175,10 +175,17 @@ export class TenantMigrator {
175
175
  // Migrate each entity type separately to avoid transaction timeouts
176
176
  for (const entity of orderedEntities) {
177
177
  await this.batchMigrator.migrateEntityDataInBatches(
178
- tenantPrisma,
179
- entity,
180
- providerId,
181
- tenantId
178
+ "public", // esquema fuente
179
+ tenantId, // esquema destino
180
+ {
181
+ type: "tenant",
182
+ idField: entity.idField,
183
+ sourceTable: entity.name,
184
+ targetTable: entity.name,
185
+ filterColumn: entity.filterColumn,
186
+ via: entity.filterVia,
187
+ },
188
+ providerId?.toString()
182
189
  );
183
190
  }
184
191
 
@@ -1,38 +1,193 @@
1
- import { ColumnSchema } from "./types";
1
+ import { PrismaClient } from "@prisma/client";
2
2
 
3
3
  export class TypecastManager {
4
- getTypecastForColumn(column: ColumnSchema): string {
5
- switch (column.data_type.toLowerCase()) {
6
- case "numeric":
7
- case "decimal":
8
- return "::numeric";
9
- case "integer":
10
- return "::integer";
11
- case "bigint":
12
- return "::bigint";
13
- case "boolean":
14
- return "::boolean";
15
- case "timestamp":
16
- return "::timestamp";
17
- case "timestamptz":
18
- return "::timestamptz";
19
- case "user-defined":
20
- // Manejar tipos enum
21
- if (column.udt_name?.startsWith("enum_")) {
22
- return `::${column.udt_name}`;
23
- }
24
- return "";
25
- default:
26
- return "";
4
+ private readonly prisma = new PrismaClient();
5
+ private readonly cache = new Map<string, Map<string, string>>(); // cache tabla -> columna -> typecast
6
+
7
+ private readonly compatibilityMap: Record<string, string[]> = {
8
+ integer: ["bigint", "smallint", "numeric", "decimal"],
9
+ bigint: ["integer", "numeric", "decimal"],
10
+ smallint: ["integer", "bigint", "numeric", "decimal"],
11
+ numeric: [
12
+ "integer",
13
+ "bigint",
14
+ "smallint",
15
+ "decimal",
16
+ "real",
17
+ "double precision",
18
+ ],
19
+ decimal: [
20
+ "numeric",
21
+ "integer",
22
+ "bigint",
23
+ "smallint",
24
+ "real",
25
+ "double precision",
26
+ ],
27
+ real: ["numeric", "decimal", "double precision"],
28
+ "double precision": ["numeric", "decimal", "real"],
29
+ "character varying": ["text", "char", "varchar"],
30
+ varchar: ["text", "character varying", "char"],
31
+ char: ["text", "character varying", "varchar"],
32
+ text: ["character varying", "varchar", "char"],
33
+ timestamp: [
34
+ "timestamptz",
35
+ "timestamp without time zone",
36
+ "timestamp with time zone",
37
+ ],
38
+ timestamptz: [
39
+ "timestamp",
40
+ "timestamp without time zone",
41
+ "timestamp with time zone",
42
+ ],
43
+ date: ["timestamp", "timestamptz"],
44
+ boolean: ["bool"],
45
+ json: ["jsonb"],
46
+ jsonb: ["json"],
47
+ };
48
+
49
+ async getTypecastForColumn(
50
+ tableName: string,
51
+ columnName: string
52
+ ): Promise<string | undefined> {
53
+ if (!this.cache.has(tableName)) {
54
+ await this.loadTableSchema(tableName);
55
+ }
56
+ const tableSchema = this.cache.get(tableName);
57
+ return tableSchema?.get(columnName);
58
+ }
59
+
60
+ private async loadTableSchema(tableName: string) {
61
+ const columns = await this.prisma.$queryRawUnsafe<any[]>(`
62
+ SELECT column_name, data_type, udt_name, is_nullable
63
+ FROM information_schema.columns
64
+ WHERE table_name = '${tableName}'
65
+ `);
66
+
67
+ const tableSchema = new Map<string, string>();
68
+
69
+ for (const column of columns) {
70
+ const { column_name, data_type, udt_name, is_nullable } = column;
71
+
72
+ let typecast: string | undefined;
73
+
74
+ switch (data_type.toLowerCase()) {
75
+ case "timestamp with time zone":
76
+ case "timestamp without time zone":
77
+ case "timestamptz":
78
+ typecast = "::timestamp with time zone";
79
+ break;
80
+ case "timestamp":
81
+ typecast = "::timestamp";
82
+ break;
83
+ case "integer":
84
+ typecast = "::integer";
85
+ break;
86
+ case "bigint":
87
+ typecast = "::bigint";
88
+ break;
89
+ case "boolean":
90
+ typecast = "::boolean";
91
+ break;
92
+ case "numeric":
93
+ case "decimal":
94
+ typecast = "::numeric";
95
+ break;
96
+ case "double precision":
97
+ typecast = "::double precision";
98
+ break;
99
+ case "text":
100
+ case "character varying":
101
+ case "varchar":
102
+ typecast = "::text";
103
+ break;
104
+ case "date":
105
+ typecast = "::date";
106
+ break;
107
+ case "time":
108
+ case "time without time zone":
109
+ typecast = "::time";
110
+ break;
111
+ case "time with time zone":
112
+ typecast = "::timetz";
113
+ break;
114
+ case "json":
115
+ typecast = "::json";
116
+ break;
117
+ case "jsonb":
118
+ typecast = "::jsonb";
119
+ break;
120
+ case "uuid":
121
+ typecast = "::uuid";
122
+ break;
123
+ default:
124
+ if (udt_name.startsWith("_")) {
125
+ // Arrays
126
+ typecast = `::${udt_name.substring(1)}[]`;
127
+ } else if (udt_name.startsWith("enum_")) {
128
+ // Enums
129
+ typecast = `::${udt_name}`;
130
+ } else {
131
+ typecast = undefined;
132
+ }
133
+ }
134
+
135
+ tableSchema.set(column_name, typecast);
27
136
  }
137
+
138
+ this.cache.set(tableName, tableSchema);
28
139
  }
29
140
 
30
- // Método para generar la expresión de CAST para un tipo enum específico
31
- getEnumCastExpression(
32
- schemaName: string,
33
- enumType: string,
34
- paramIndex: number
35
- ): string {
36
- return `CAST($${paramIndex} AS "${schemaName}".${enumType})`;
141
+ areTypesCompatible(sourceType: string, targetType: string): boolean {
142
+ // Si los tipos son iguales, son compatibles
143
+ if (sourceType.toLowerCase() === targetType.toLowerCase()) {
144
+ return true;
145
+ }
146
+
147
+ // Verificar si el tipo fuente puede convertirse al tipo destino
148
+ const compatibleTypes =
149
+ this.compatibilityMap[sourceType.toLowerCase()] || [];
150
+ return compatibleTypes.includes(targetType.toLowerCase());
151
+ }
152
+
153
+ castValue(value: any, sourceType: string, targetType: string): any {
154
+ if (value === null || value === undefined) {
155
+ return null;
156
+ }
157
+
158
+ try {
159
+ switch (targetType.toLowerCase()) {
160
+ case "integer":
161
+ case "bigint":
162
+ case "smallint":
163
+ return parseInt(value, 10);
164
+ case "numeric":
165
+ case "decimal":
166
+ case "real":
167
+ case "double precision":
168
+ return parseFloat(value);
169
+ case "boolean":
170
+ return Boolean(value);
171
+ case "json":
172
+ case "jsonb":
173
+ return typeof value === "string" ? JSON.parse(value) : value;
174
+ case "timestamp":
175
+ case "timestamptz":
176
+ return new Date(value).toISOString();
177
+ case "date":
178
+ return new Date(value).toISOString().split("T")[0];
179
+ case "text":
180
+ case "character varying":
181
+ case "varchar":
182
+ case "char":
183
+ return String(value);
184
+ default:
185
+ return value;
186
+ }
187
+ } catch (error) {
188
+ throw new Error(
189
+ `Error al convertir valor "${value}" de ${sourceType} a ${targetType}: ${error.message}`
190
+ );
191
+ }
37
192
  }
38
193
  }
@@ -1,11 +1,19 @@
1
1
  import { Pool } from "pg";
2
2
  import { PrismaClient } from "@prisma/client";
3
3
 
4
+ export interface DatabaseConnection {
5
+ pool?: Pool;
6
+ prisma?: PrismaClient;
7
+ query: (sql: string, params?: any[]) => Promise<any>;
8
+ sourceId: string; // Identificador único para la fuente (ej: "colombia", "rd", "guatemala")
9
+ }
10
+
4
11
  export interface DatabaseConnections {
5
- sourcePool: Pool;
12
+ sourceConnections?: DatabaseConnection[];
13
+ sourcePool?: Pool;
6
14
  targetPool: Pool;
7
- sourcePrisma: PrismaClient;
8
15
  targetPrisma: PrismaClient;
16
+ sourcePrisma?: PrismaClient;
9
17
  }
10
18
 
11
19
  export interface ColumnSchema {
@@ -21,10 +29,10 @@ export interface ColumnSchema {
21
29
 
22
30
  // Updated EntityType to include filter configuration
23
31
  export interface EntityType {
24
- name: string; // Table name
25
- idField: string; // Primary key column name
32
+ name: string; // Table name
33
+ idField: string; // Primary key column name
26
34
  filterColumn?: string; // Column used for filtering (either directly or via join)
27
- filterVia?: string; // Intermediate table for filtering join (if needed)
35
+ filterVia?: string; // Intermediate table for filtering join (if needed)
28
36
  // Keep optional legacy fields for potential backward compatibility or specific use cases
29
37
  foreignKey?: string;
30
38
  joinTable?: string;
@@ -38,3 +46,68 @@ export interface EnumCastValue {
38
46
  value: any;
39
47
  enumType: string;
40
48
  }
49
+
50
+ export interface MigrationOptions {
51
+ publicOnly?: boolean;
52
+ multiTenant?: boolean;
53
+ sourceSchema?: string;
54
+ targetSchema?: string;
55
+ forceSingleTenant?: boolean;
56
+ configPath?: string;
57
+ batchSize?: number;
58
+ retryAttempts?: number;
59
+ validateData?: boolean;
60
+ logLevel?: "debug" | "info" | "warn" | "error";
61
+ }
62
+
63
+ export interface ColumnConfig {
64
+ sourceColumn?: string;
65
+ targetColumn?: string;
66
+ type?: string;
67
+ nullable?: boolean;
68
+ defaultValue?: any;
69
+ transform?: (value: any) => any;
70
+ }
71
+
72
+ export interface TableConfig {
73
+ type: "public" | "filteredPublic" | "tenantInfo" | "tenant";
74
+ idField: string;
75
+ filterColumn?: string;
76
+ via?: string;
77
+ providerLink?: string;
78
+ tenantKey?: string;
79
+ dependencies?: string[];
80
+ sourceTable?: string;
81
+ targetTable?: string;
82
+ columns?: Record<string, ColumnConfig>;
83
+ skipIfExists?: boolean;
84
+ batchSize?: number;
85
+ }
86
+
87
+ export interface ExtendedTableConfig extends TableConfig {
88
+ processed?: boolean;
89
+ retryCount?: number;
90
+ lastError?: Error;
91
+ }
92
+
93
+ export interface ExtendedMigrationConfig {
94
+ tables: Record<string, ExtendedTableConfig>;
95
+ version: string;
96
+ sourceSchema?: string;
97
+ targetSchema?: string;
98
+ }
99
+
100
+ export interface MigrationConfig {
101
+ commonSchema: string;
102
+ tables: Record<string, TableConfig>;
103
+ tenantInfo: {
104
+ sourceTable: string;
105
+ tenantIdColumn: string;
106
+ providerIdColumn: string;
107
+ };
108
+ migrationPriorities?: {
109
+ high?: string[];
110
+ medium?: string[];
111
+ low?: string[];
112
+ };
113
+ }
@@ -0,0 +1,49 @@
1
+ import { PrismaClient } from "@prisma/client";
2
+ import { exec } from "child_process";
3
+ import { promisify } from "util";
4
+
5
+ const execAsync = promisify(exec);
6
+
7
+ export class DatabaseInitializer {
8
+ constructor(private readonly databaseUrl: string) {}
9
+
10
+ async initialize(): Promise<void> {
11
+ try {
12
+ console.log("Checking if database needs initialization...");
13
+
14
+ const { Pool } = await import("pg");
15
+ const pool = new Pool({ connectionString: this.databaseUrl });
16
+
17
+ try {
18
+ // Verificar si existen tablas en la base de datos
19
+ const result = await pool.query(`
20
+ SELECT COUNT(*)
21
+ FROM information_schema.tables
22
+ WHERE table_schema = 'public'
23
+ AND table_type = 'BASE TABLE'
24
+ `);
25
+
26
+ const tableCount = parseInt(result.rows[0].count);
27
+
28
+ if (tableCount === 0) {
29
+ console.log(
30
+ "No tables found in database. Running initial migration..."
31
+ );
32
+ // Ejecutar migración inicial usando Prisma
33
+ await execAsync("npx prisma migrate deploy");
34
+ await execAsync("npx prisma db push");
35
+ console.log("Initial database migration completed successfully.");
36
+ } else {
37
+ console.log(
38
+ `Database already contains ${tableCount} tables. Skipping initialization.`
39
+ );
40
+ }
41
+ } finally {
42
+ await pool.end();
43
+ }
44
+ } catch (error) {
45
+ console.error("Error initializing database:", error);
46
+ throw error;
47
+ }
48
+ }
49
+ }