@javalabs/prisma-client 1.0.4 → 1.0.6

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 (58) 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/fix-table-indexes.d.ts +26 -0
  33. package/dist/scripts/fix-table-indexes.js +460 -0
  34. package/dist/scripts/fix-table-indexes.js.map +1 -0
  35. package/dist/scripts/multi-db-migration.d.ts +1 -0
  36. package/dist/scripts/multi-db-migration.js +55 -0
  37. package/dist/scripts/multi-db-migration.js.map +1 -0
  38. package/dist/scripts/run-migration.js +41 -75
  39. package/dist/scripts/run-migration.js.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/migration-config.json +40 -72
  42. package/{migration-config-public.json → migration-config.json.bk} +14 -14
  43. package/package.json +6 -3
  44. package/src/scripts/data-migration/batch-migrator.ts +192 -513
  45. package/src/scripts/data-migration/data-transformer.ts +252 -203
  46. package/src/scripts/data-migration/db-connector.ts +66 -13
  47. package/src/scripts/data-migration/dependency-resolver.ts +121 -266
  48. package/src/scripts/data-migration/foreign-key-manager.ts +214 -32
  49. package/src/scripts/data-migration/migration-config.json +63 -0
  50. package/src/scripts/data-migration/migration-tool.ts +377 -225
  51. package/src/scripts/data-migration/schema-utils.ts +94 -32
  52. package/src/scripts/data-migration/tenant-migrator.ts +12 -5
  53. package/src/scripts/data-migration/typecast-manager.ts +186 -31
  54. package/src/scripts/data-migration/types.ts +78 -5
  55. package/src/scripts/dumps/source_dump_20250428_145606.sql +323 -0
  56. package/src/scripts/fix-table-indexes.ts +602 -0
  57. package/src/scripts/post-migration-validator.ts +206 -107
  58. package/src/scripts/run-migration.ts +87 -101
@@ -1,6 +1,6 @@
1
1
  import { Logger } from "@nestjs/common";
2
- import * as fs from 'fs';
3
- import * as path from 'path';
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
4
  import { SchemaUtils } from "./schema-utils";
5
5
  import { DataTransformer } from "./data-transformer";
6
6
  import { DatabaseConnections, ColumnSchema, EntityType } from "./types";
@@ -11,19 +11,22 @@ import { DependencyResolver } from "./dependency-resolver";
11
11
  interface MigrationOptions {
12
12
  publicOnly?: boolean;
13
13
  multiTenant?: boolean;
14
- sourceSchema?: string; // Potentially deprecated with config
15
- targetSchema?: string; // Potentially deprecated with config
14
+ sourceSchema?: string;
15
+ targetSchema?: string;
16
16
  forceSingleTenant?: boolean; // Alias for publicOnly
17
17
  configPath?: string;
18
18
  }
19
19
 
20
20
  interface TableConfig {
21
- type: 'public' | 'filteredPublic' | 'tenantInfo' | 'tenant';
21
+ type: "public" | "filteredPublic" | "tenantInfo" | "tenant";
22
22
  idField: string;
23
23
  filterColumn?: string;
24
24
  via?: string;
25
25
  providerLink?: string;
26
26
  tenantKey?: string;
27
+ dependencies?: string[];
28
+ sourceTable: string;
29
+ targetTable: string;
27
30
  }
28
31
 
29
32
  interface MigrationConfig {
@@ -40,61 +43,74 @@ export class DataMigrationTool {
40
43
  private readonly logger = new Logger("DataMigrationTool");
41
44
  private readonly schemaUtils: SchemaUtils;
42
45
  private readonly dataTransformer: DataTransformer;
43
- private readonly foreignKeyManager: ForeignKeyManager;
44
46
  private readonly batchMigrator: BatchMigrator;
47
+ private readonly foreignKeyManager: ForeignKeyManager;
45
48
  private readonly dependencyResolver: DependencyResolver;
46
- private migrationConfig: MigrationConfig;
47
-
48
- private schemaCache: Record<string, ColumnSchema[]> = {};
49
- private targetSchemaCache: Record<string, ColumnSchema[]> = {};
50
49
 
50
+ constructor(
51
+ private readonly connections: DatabaseConnections,
52
+ private migrationConfig: MigrationConfig,
53
+ private readonly options: MigrationOptions = {}
54
+ ) {
55
+ this.schemaUtils = new SchemaUtils(connections);
51
56
 
52
- constructor(private readonly connections: DatabaseConnections) {
53
- this.schemaUtils = new SchemaUtils(
57
+ this.dataTransformer = new DataTransformer(
58
+ this.schemaUtils,
54
59
  connections.sourcePool,
55
- connections.targetPool
60
+ connections
56
61
  );
57
- this.dataTransformer = new DataTransformer(this.schemaUtils);
62
+
58
63
  this.foreignKeyManager = new ForeignKeyManager(connections);
59
- this.dependencyResolver = new DependencyResolver(
60
- connections.sourcePool,
61
- connections.targetPool
62
- );
64
+
63
65
  this.batchMigrator = new BatchMigrator(
64
- this.dataTransformer,
65
66
  this.schemaUtils,
67
+ this.dataTransformer,
66
68
  connections,
67
- this.dependencyResolver,
68
- this.schemaCache,
69
- this.targetSchemaCache
69
+ this.options
70
+ );
71
+
72
+ this.dependencyResolver = new DependencyResolver(
73
+ connections.sourcePool,
74
+ connections.targetPool
70
75
  );
71
76
  }
72
77
 
73
- private loadConfig(configPath: string = 'migration-config.json'): void {
78
+ private loadConfig(configPath: string = "migration-config.json"): void {
74
79
  const fullPath = path.resolve(process.cwd(), configPath);
75
80
  if (!fs.existsSync(fullPath)) {
76
81
  throw new Error(`Migration config file not found at: ${fullPath}`);
77
82
  }
78
83
  try {
79
- const fileContent = fs.readFileSync(fullPath, 'utf-8');
84
+ const fileContent = fs.readFileSync(fullPath, "utf-8");
80
85
  this.migrationConfig = JSON.parse(fileContent);
81
86
  this.logger.log(`Loaded migration config from: ${fullPath}`);
87
+
88
+ // Pasar la configuración al DependencyResolver
89
+ this.dependencyResolver.setConfig(this.migrationConfig);
82
90
  } catch (error) {
83
- throw new Error(`Error reading or parsing migration config file: ${error.message}`);
91
+ throw new Error(
92
+ `Error reading or parsing migration config file: ${error.message}`
93
+ );
84
94
  }
85
- if (!this.migrationConfig.commonSchema || !this.migrationConfig.tables || !this.migrationConfig.tenantInfo) {
86
- throw new Error('Invalid migration config structure.');
95
+ if (
96
+ !this.migrationConfig.commonSchema ||
97
+ !this.migrationConfig.tables ||
98
+ !this.migrationConfig.tenantInfo
99
+ ) {
100
+ throw new Error("Invalid migration config structure.");
87
101
  }
88
102
  }
89
103
 
90
- async migrateData(options: MigrationOptions = {}) {
104
+ async migrate(): Promise<void> {
91
105
  try {
92
106
  this.logger.log("Starting data migration process");
93
- this.loadConfig(options.configPath);
107
+ this.loadConfig(this.options.configPath);
94
108
 
95
109
  // Check the publicOnly/forceSingleTenant flag AFTER loading config
96
- if (options.forceSingleTenant || options.publicOnly) {
97
- this.logger.log("Public-only migration requested. Migrating only 'public' type tables...");
110
+ if (this.options.forceSingleTenant || this.options.publicOnly) {
111
+ this.logger.log(
112
+ "Public-only migration requested. Migrating only 'public' type tables..."
113
+ );
98
114
  await this.migratePublicTablesOnly(); // Call the dedicated function
99
115
  } else {
100
116
  this.logger.log("Multi-tenant migration requested based on config...");
@@ -107,251 +123,387 @@ export class DataMigrationTool {
107
123
  }
108
124
  }
109
125
 
110
- // --- Function for Public-Only Migration ---
126
+ // --- Function for Public-Only Migration ---
111
127
  private async migratePublicTablesOnly() {
112
- const { commonSchema, tables: tablesConfig } = this.migrationConfig;
113
- const allTablesInOrder = await this.dependencyResolver.analyzeDependencies();
128
+ const { commonSchema, tables: tablesConfig } = this.migrationConfig;
129
+ const allTablesInOrder =
130
+ await this.dependencyResolver.analyzeDependencies();
114
131
 
115
- // Filter only the tables marked as 'public' in the config
116
- const publicTablesToMigrate = allTablesInOrder.filter(t => tablesConfig[t]?.type === 'public');
132
+ // Filter only the tables marked as 'public' in the config
133
+ const publicTablesToMigrate = allTablesInOrder.filter(
134
+ (t) => tablesConfig[t]?.type === "public"
135
+ );
117
136
 
118
- this.logger.log(`Migrating public tables to '${commonSchema}' schema: ${publicTablesToMigrate.join(", ")}`);
119
- if (publicTablesToMigrate.length === 0) {
120
- this.logger.warn("No tables configured as 'public'. Public-only migration will not process any tables.");
121
- return;
122
- }
137
+ this.logger.log(
138
+ `Migrating public tables to '${commonSchema}' schema: ${publicTablesToMigrate.join(
139
+ ", "
140
+ )}`
141
+ );
142
+ if (publicTablesToMigrate.length === 0) {
143
+ this.logger.warn(
144
+ "No tables configured as 'public'. Public-only migration will not process any tables."
145
+ );
146
+ return;
147
+ }
123
148
 
124
- await this.connections.targetPool.query(`SET session_replication_role = 'replica';`);
149
+ await this.connections.targetPool.query(
150
+ `SET session_replication_role = 'replica';`
151
+ );
125
152
 
126
- for (const tableName of publicTablesToMigrate) {
127
- const config = tablesConfig[tableName];
128
- if (!config) {
129
- this.logger.warn(`Config not found for public table ${tableName}, skipping.`); // Should not happen if filter worked
130
- continue;
131
- }
132
- try {
133
- await this.batchMigrator.migrateEntityDataInBatches(
134
- this.connections.targetPrisma,
135
- { name: tableName, idField: config.idField },
136
- null, // No provider filter for public-only migration
137
- commonSchema // Target is the common schema
138
- );
139
- } catch (error) {
140
- this.logger.error(`Error migrating public table ${tableName}: ${error.message}`);
141
- // Decide whether to stop or continue on error for public-only
142
- throw error;
143
- }
153
+ for (const tableName of publicTablesToMigrate) {
154
+ const config = tablesConfig[tableName];
155
+ if (!config) {
156
+ this.logger.warn(
157
+ `Config not found for public table ${tableName}, skipping.`
158
+ ); // Should not happen if filter worked
159
+ continue;
144
160
  }
161
+ try {
162
+ await this.batchMigrator.migrateEntityDataInBatches(
163
+ this.migrationConfig.commonSchema,
164
+ commonSchema,
165
+ {
166
+ type: "public",
167
+ idField: config.idField,
168
+ sourceTable: tableName,
169
+ targetTable: tableName,
170
+ },
171
+ null // No provider filter for public-only migration
172
+ );
173
+ } catch (error) {
174
+ this.logger.error(
175
+ `Error migrating public table ${tableName}: ${error.message}`
176
+ );
177
+ // Decide whether to stop or continue on error for public-only
178
+ throw error;
179
+ }
180
+ }
145
181
 
146
- await this.connections.targetPool.query(`SET session_replication_role = 'origin';`);
147
- this.logger.log("Finished migrating public tables.");
182
+ await this.connections.targetPool.query(
183
+ `SET session_replication_role = 'origin';`
184
+ );
185
+ this.logger.log("Finished migrating public tables.");
148
186
  }
149
187
 
150
-
151
188
  // Keep validateSourceData (potentially enhanced)
152
189
  private async validateSourceData() {
153
- this.logger.log("Validating source data...");
154
- try {
155
- const { sourceTable, providerIdColumn } = this.migrationConfig.tenantInfo;
156
- const tablesConfig = this.migrationConfig.tables;
157
-
158
- // Find the config for the transaction-like table and the provider table
159
- let transactionTableKey = 'transactions'; // Default or find dynamically
160
- let providerTableKey = 'providers'; // Default or find dynamically
161
- let transactionProviderFk = providerIdColumn; // Default
162
-
163
- for (const key in tablesConfig) {
164
- if (tablesConfig[key].type === 'filteredPublic' && tablesConfig[key].filterColumn === providerIdColumn && !tablesConfig[key].via) {
165
- transactionTableKey = key;
166
- transactionProviderFk = tablesConfig[key].filterColumn;
167
- }
168
- if (tablesConfig[key].type === 'public' && tablesConfig[key].idField === providerIdColumn) {
169
- providerTableKey = key;
170
- }
171
- }
172
- this.logger.debug(`Validation check: Using ${transactionTableKey}.${transactionProviderFk} referencing ${providerTableKey}.${providerIdColumn}`);
190
+ this.logger.log("Validating source data...");
191
+ try {
192
+ const { sourceTable, providerIdColumn } = this.migrationConfig.tenantInfo;
193
+ const tablesConfig = this.migrationConfig.tables;
194
+
195
+ // Find the config for the transaction-like table and the provider table
196
+ let transactionTableKey = "transactions"; // Default or find dynamically
197
+ let providerTableKey = "providers"; // Default or find dynamically
198
+ let transactionProviderFk = providerIdColumn; // Default
199
+
200
+ for (const key in tablesConfig) {
201
+ if (
202
+ tablesConfig[key].type === "filteredPublic" &&
203
+ tablesConfig[key].filterColumn === providerIdColumn &&
204
+ !tablesConfig[key].via
205
+ ) {
206
+ transactionTableKey = key;
207
+ transactionProviderFk = tablesConfig[key].filterColumn;
208
+ }
209
+ if (
210
+ tablesConfig[key].type === "public" &&
211
+ tablesConfig[key].idField === providerIdColumn
212
+ ) {
213
+ providerTableKey = key;
214
+ }
215
+ }
216
+ this.logger.debug(
217
+ `Validation check: Using ${transactionTableKey}.${transactionProviderFk} referencing ${providerTableKey}.${providerIdColumn}`
218
+ );
173
219
 
174
- // Dynamically build the query
175
- // IMPORTANT: Ensure proper SQL identifier quoting if names contain special chars/are reserved words
176
- const query = `
220
+ // Dynamically build the query
221
+ // IMPORTANT: Ensure proper SQL identifier quoting if names contain special chars/are reserved words
222
+ const query = `
177
223
  SELECT t."${tablesConfig[transactionTableKey].idField}" AS transaction_id, t."${transactionProviderFk}"
178
224
  FROM "${transactionTableKey}" t
179
225
  LEFT JOIN "${providerTableKey}" p ON t."${transactionProviderFk}" = p."${providerIdColumn}"
180
226
  WHERE t."${transactionProviderFk}" IS NOT NULL AND p."${providerIdColumn}" IS NULL;
181
227
  `;
182
228
 
183
- const invalidTransactions = (await this.connections.sourcePrisma.$queryRawUnsafe(query)) as any[];
184
-
185
- if (invalidTransactions.length > 0) {
186
- this.logger.error(`${invalidTransactions.length} records found in ${transactionTableKey} with invalid foreign key reference to ${providerTableKey}.${providerIdColumn}`);
187
- console.error("Invalid records (limit 10):", invalidTransactions.slice(0, 10));
188
- throw new Error("Source data validation failed: Invalid foreign key references found. Please check the configuration or fix the source data.");
189
- } else {
190
- this.logger.log(`Source data validation passed: All ${transactionTableKey}.${transactionProviderFk} references are valid.`);
191
- }
192
- } catch (error) {
193
- this.logger.error("Error during source data validation:");
194
- console.error(error);
195
- throw error;
196
- }
229
+ const invalidTransactions =
230
+ (await this.connections.sourcePrisma.$queryRawUnsafe(query)) as any[];
231
+
232
+ if (invalidTransactions.length > 0) {
233
+ this.logger.error(
234
+ `${invalidTransactions.length} records found in ${transactionTableKey} with invalid foreign key reference to ${providerTableKey}.${providerIdColumn}`
235
+ );
236
+ console.error(
237
+ "Invalid records (limit 10):",
238
+ invalidTransactions.slice(0, 10)
239
+ );
240
+ throw new Error(
241
+ "Source data validation failed: Invalid foreign key references found. Please check the configuration or fix the source data."
242
+ );
243
+ } else {
244
+ this.logger.log(
245
+ `Source data validation passed: All ${transactionTableKey}.${transactionProviderFk} references are valid.`
246
+ );
247
+ }
248
+ } catch (error) {
249
+ this.logger.error("Error during source data validation:");
250
+ console.error(error);
251
+ throw error;
252
+ }
197
253
  }
198
254
 
199
-
200
255
  // Renamed from migrateToTenantSchemas to reflect its purpose
201
256
  private async migrateMultiTenantWithConfig() {
202
- await this.validateSourceData(); // Validate first
257
+ await this.validateSourceData();
258
+
259
+ const {
260
+ commonSchema,
261
+ tables: tablesConfig,
262
+ tenantInfo,
263
+ } = this.migrationConfig;
264
+
265
+ // Obtener orden de tablas basado en dependencias
266
+ const allTablesInOrder =
267
+ await this.dependencyResolver.analyzeDependencies();
268
+ this.logger.log(`Orden de migración: ${allTablesInOrder.join(" -> ")}`);
269
+
270
+ // Validar dependencias antes de comenzar
271
+ for (const tableName of allTablesInOrder) {
272
+ const config = tablesConfig[tableName];
273
+ if (config?.dependencies) {
274
+ const valid = await this.dependencyResolver.validateDependencies(
275
+ tableName,
276
+ config.dependencies
277
+ );
278
+ if (!valid) {
279
+ this.logger.warn(
280
+ `Advertencia: Algunas dependencias para ${tableName} no están disponibles`
281
+ );
282
+ }
283
+ }
284
+ }
203
285
 
204
- const { commonSchema, tables: tablesConfig, tenantInfo } = this.migrationConfig;
205
- const allTablesInOrder = await this.dependencyResolver.analyzeDependencies();
286
+ // --- PASADA 0: Migrar tablas públicas ---
287
+ this.logger.log(`Migrando tablas públicas al esquema '${commonSchema}'...`);
288
+ await this.connections.targetPool.query(
289
+ `SET session_replication_role = 'replica';`
290
+ );
206
291
 
207
- // --- PASADA 0: Migrar tablas públicas ---
208
- this.logger.log(`Migrating public tables to '${commonSchema}' schema...`);
209
- await this.connections.targetPool.query(`SET session_replication_role = 'replica';`);
210
- const publicTables = allTablesInOrder.filter(t => tablesConfig[t]?.type === 'public');
292
+ const publicTables = allTablesInOrder.filter(
293
+ (t) => tablesConfig[t]?.type === "public"
294
+ );
211
295
 
212
296
  for (const tableName of publicTables) {
213
- const config = tablesConfig[tableName];
214
- if (!config) {
215
- this.logger.warn(`Skipping migration for table ${tableName}: Not found in config.`);
216
- continue;
217
- }
218
- try {
219
- await this.batchMigrator.migrateEntityDataInBatches(
220
- this.connections.targetPrisma,
221
- { name: tableName, idField: config.idField }, // Pass config details
222
- null, // No provider filter for public tables
223
- commonSchema
224
- );
225
- } catch (error) {
226
- this.logger.error(`Error migrating public table ${tableName}: ${error.message}`);
227
- throw error; // Stop if essential public table fails
228
- }
297
+ const config = tablesConfig[tableName];
298
+ if (!config) {
299
+ this.logger.warn(
300
+ `Saltando migración de tabla ${tableName}: No encontrada en configuración`
301
+ );
302
+ continue;
303
+ }
304
+
305
+ try {
306
+ await this.batchMigrator.migrateEntityDataInBatches(
307
+ this.migrationConfig.commonSchema,
308
+ commonSchema,
309
+ {
310
+ type: "public",
311
+ idField: config.idField,
312
+ sourceTable: tableName,
313
+ targetTable: tableName,
314
+ },
315
+ null
316
+ );
317
+ } catch (error) {
318
+ this.logger.error(
319
+ `Error migrando tabla pública ${tableName}: ${error.message}`
320
+ );
321
+ throw error;
322
+ }
229
323
  }
230
- await this.connections.targetPool.query(`SET session_replication_role = 'origin';`);
324
+ await this.connections.targetPool.query(
325
+ `SET session_replication_role = 'origin';`
326
+ );
231
327
  this.logger.log("Finished migrating public tables.");
232
328
 
233
- // --- Obtener Inquilinos ---
234
- this.logger.log(`Fetching tenant info from source table '${tenantInfo.sourceTable}'...`);
235
- const tenants = await this.connections.sourcePrisma[tenantInfo.sourceTable].findMany({
236
- select: {
237
- [tenantInfo.tenantIdColumn]: true,
238
- [tenantInfo.providerIdColumn]: true,
239
- }
329
+ // --- Obtener Inquilinos ---
330
+ this.logger.log(
331
+ `Fetching tenant info from source table '${tenantInfo.sourceTable}'...`
332
+ );
333
+ const tenants = await this.connections.sourcePrisma[
334
+ tenantInfo.sourceTable
335
+ ].findMany({
336
+ select: {
337
+ [tenantInfo.tenantIdColumn]: true,
338
+ [tenantInfo.providerIdColumn]: true,
339
+ },
240
340
  });
241
341
  this.logger.log(`Found ${tenants.length} potential tenants.`);
242
342
 
243
- // --- Crear Esquemas y Preparar Migración por Inquilino ---
343
+ // --- Crear Esquemas y Preparar Migración por Inquilino ---
244
344
  const validTenants = new Map<string, number | null>(); // Map tenantId -> providerId
245
- this.logger.log("Ensuring tenant schemas exist (if strategy requires them)...");
246
- let requiresTenantSchemas = Object.values(tablesConfig).some(t => t.type === 'tenant');
247
- if(requiresTenantSchemas) {
248
- this.logger.log('Tenant-specific schemas are required by config.');
249
- for (const tenant of tenants) {
250
- const tenantId = tenant[tenantInfo.tenantIdColumn];
251
- const providerId = tenant[tenantInfo.providerIdColumn] ?? null;
252
- if (tenantId && typeof tenantId === 'string' && tenantId !== commonSchema) {
253
- await this.schemaUtils.createSchema(tenantId);
254
- validTenants.set(tenantId, providerId);
255
- this.logger.log(`Ensured schema exists for tenant: ${tenantId}`);
256
- } else {
257
- this.logger.warn(`Skipping invalid/public tenant ID: ${tenantId}`);
258
- }
345
+ this.logger.log(
346
+ "Ensuring tenant schemas exist (if strategy requires them)..."
347
+ );
348
+ let requiresTenantSchemas = Object.values(tablesConfig).some(
349
+ (t) => t.type === "tenant"
350
+ );
351
+ if (requiresTenantSchemas) {
352
+ this.logger.log("Tenant-specific schemas are required by config.");
353
+ for (const tenant of tenants) {
354
+ const tenantId = tenant[tenantInfo.tenantIdColumn];
355
+ const providerId = tenant[tenantInfo.providerIdColumn] ?? null;
356
+ if (
357
+ tenantId &&
358
+ typeof tenantId === "string" &&
359
+ tenantId !== commonSchema
360
+ ) {
361
+ await this.schemaUtils.createSchema(tenantId);
362
+ validTenants.set(tenantId, providerId);
363
+ this.logger.log(`Ensured schema exists for tenant: ${tenantId}`);
364
+ } else {
365
+ this.logger.warn(`Skipping invalid/public tenant ID: ${tenantId}`);
259
366
  }
367
+ }
260
368
  } else {
261
- this.logger.log('No tables configured with type "tenant". Tenant schema creation skipped.');
262
- // Still populate validTenants map for filteredPublic logic
263
- for (const tenant of tenants) {
264
- const tenantId = tenant[tenantInfo.tenantIdColumn];
265
- const providerId = tenant[tenantInfo.providerIdColumn] ?? null;
266
- // Use tenantId as key even if schemas aren't created, it represents the logical tenant
267
- if (tenantId && typeof tenantId === 'string') {
268
- validTenants.set(tenantId, providerId);
269
- }
270
- }
369
+ this.logger.log(
370
+ 'No tables configured with type "tenant". Tenant schema creation skipped.'
371
+ );
372
+ // Still populate validTenants map for filteredPublic logic
373
+ for (const tenant of tenants) {
374
+ const tenantId = tenant[tenantInfo.tenantIdColumn];
375
+ const providerId = tenant[tenantInfo.providerIdColumn] ?? null;
376
+ // Use tenantId as key even if schemas aren't created, it represents the logical tenant
377
+ if (tenantId && typeof tenantId === "string") {
378
+ validTenants.set(tenantId, providerId);
379
+ }
380
+ }
271
381
  }
272
- this.logger.log(`Prepared ${validTenants.size} valid tenants for migration.`);
382
+ this.logger.log(
383
+ `Prepared ${validTenants.size} valid tenants for migration.`
384
+ );
273
385
 
274
- // --- PASADAS POR INQUILINO (para tablas filteredPublic y tenant) ---
275
- const tablesToMigratePerTenant = allTablesInOrder.filter(t =>
276
- tablesConfig[t]?.type === 'filteredPublic' || tablesConfig[t]?.type === 'tenant'
386
+ // --- PASADAS POR INQUILINO (para tablas filteredPublic y tenant) ---
387
+ const tablesToMigratePerTenant = allTablesInOrder.filter(
388
+ (t) =>
389
+ tablesConfig[t]?.type === "filteredPublic" ||
390
+ tablesConfig[t]?.type === "tenant"
391
+ );
392
+ this.logger.log(
393
+ `Migrating filtered/tenant tables: ${tablesToMigratePerTenant.join(", ")}`
277
394
  );
278
- this.logger.log(`Migrating filtered/tenant tables: ${tablesToMigratePerTenant.join(", ")}`);
279
395
 
280
396
  for (const [tenantId, providerId] of validTenants.entries()) {
281
- // Note: tenantId here might just be logical identifier if not creating schemas
282
- this.logger.log(`Starting migration pass related to tenant: ${tenantId} (Filtering by Provider ID: ${providerId ?? 'N/A'})`);
283
- await this.connections.targetPool.query(`SET session_replication_role = 'replica';`);
284
-
285
- for (const tableName of tablesToMigratePerTenant) {
286
- const config = tablesConfig[tableName];
287
- if (!config) {
288
- this.logger.warn(`Skipping migration for table ${tableName} for tenant ${tenantId}: Not found in config.`);
289
- continue;
290
- }
291
-
292
- // Determine schema destino basado en config.type
293
- const targetSchemaForTable = config.type === 'tenant' ? tenantId : commonSchema;
294
-
295
- try {
296
- const entity: EntityType = {
297
- name: tableName,
298
- idField: config.idField,
299
- filterColumn: config.filterColumn,
300
- filterVia: config.via
301
- };
302
-
303
- await this.batchMigrator.migrateEntityDataInBatches(
304
- this.connections.targetPrisma,
305
- entity,
306
- providerId, // providerId para FILTRAR origen
307
- targetSchemaForTable // esquema donde INSERTAR
308
- );
309
- } catch (error) {
310
- this.logger.error(`Error migrating table ${tableName} into schema ${targetSchemaForTable} (related to tenant ${tenantId}): ${error.message}`);
311
- }
397
+ // Note: tenantId here might just be logical identifier if not creating schemas
398
+ this.logger.log(
399
+ `Starting migration pass related to tenant: ${tenantId} (Filtering by Provider ID: ${
400
+ providerId ?? "N/A"
401
+ })`
402
+ );
403
+ await this.connections.targetPool.query(
404
+ `SET session_replication_role = 'replica';`
405
+ );
406
+
407
+ for (const tableName of tablesToMigratePerTenant) {
408
+ const config = tablesConfig[tableName];
409
+ if (!config) {
410
+ this.logger.warn(
411
+ `Skipping migration for table ${tableName} for tenant ${tenantId}: Not found in config.`
412
+ );
413
+ continue;
312
414
  }
313
- await this.connections.targetPool.query(`SET session_replication_role = 'origin';`);
314
- this.logger.log(`Finished migration pass related to tenant: ${tenantId}`);
415
+
416
+ // Determine schema destino basado en config.type
417
+ const targetSchemaForTable =
418
+ config.type === "tenant" ? tenantId : commonSchema;
419
+
420
+ try {
421
+ const entity: EntityType = {
422
+ name: tableName,
423
+ idField: config.idField,
424
+ filterColumn: config.filterColumn,
425
+ filterVia: config.via,
426
+ };
427
+
428
+ const tableConfig: TableConfig = {
429
+ type: entity.filterVia ? "filteredPublic" : "tenant",
430
+ idField: entity.idField,
431
+ filterColumn: entity.filterColumn,
432
+ via: entity.filterVia,
433
+ sourceTable: entity.name,
434
+ targetTable: entity.name,
435
+ };
436
+
437
+ await this.batchMigrator.migrateEntityDataInBatches(
438
+ this.migrationConfig.commonSchema,
439
+ targetSchemaForTable,
440
+ tableConfig,
441
+ providerId?.toString() // Convertir providerId a string
442
+ );
443
+ } catch (error) {
444
+ this.logger.error(
445
+ `Error migrating table ${tableName} into schema ${targetSchemaForTable} (related to tenant ${tenantId}): ${error.message}`
446
+ );
447
+ }
448
+ }
449
+ await this.connections.targetPool.query(
450
+ `SET session_replication_role = 'origin';`
451
+ );
452
+ this.logger.log(`Finished migration pass related to tenant: ${tenantId}`);
315
453
  }
316
454
 
317
455
  this.logger.log("Data migration process completed.");
318
456
  // await this.verifyMigration(Array.from(validTenants.keys()));
319
457
  }
320
458
 
321
- private async verifyMigration(tenantIds: string[]) {
322
- this.logger.log("Verifying migrated data...");
323
- // Verificar commonSchema
459
+ private async verifyMigration(tenantIds: string[]) {
460
+ this.logger.log("Verifying migrated data...");
461
+ // Verificar commonSchema
462
+ try {
463
+ const publicResult = await this.connections.targetPool.query(
464
+ `SELECT tablename, n_live_tup FROM pg_stat_user_tables WHERE schemaname = $1`,
465
+ [this.migrationConfig.commonSchema]
466
+ );
467
+ this.logger.log(
468
+ `Verification for schema '${this.migrationConfig.commonSchema}':`
469
+ );
470
+ publicResult.rows.forEach((row) =>
471
+ this.logger.log(` Table: ${row.tablename}, Rows: ${row.n_live_tup}`)
472
+ );
473
+ } catch (err) {
474
+ this.logger.error(`Error verifying public schema: ${err.message}`);
475
+ }
476
+
477
+ // Verificar schemas de inquilinos (only if they were supposed to be created)
478
+ let requiresTenantSchemas = Object.values(this.migrationConfig.tables).some(
479
+ (t) => t.type === "tenant"
480
+ );
481
+ if (requiresTenantSchemas) {
482
+ for (const tenantId of tenantIds) {
324
483
  try {
325
- const publicResult = await this.connections.targetPool.query(
484
+ const tenantResult = await this.connections.targetPool.query(
326
485
  `SELECT tablename, n_live_tup FROM pg_stat_user_tables WHERE schemaname = $1`,
327
- [this.migrationConfig.commonSchema]
486
+ [tenantId]
487
+ );
488
+ this.logger.log(`Verification for tenant schema '${tenantId}':`);
489
+ if (tenantResult.rows.length === 0) {
490
+ this.logger.warn(
491
+ ` Schema ${tenantId} has no tables (or pg_stat_user_tables is empty).`
492
+ );
493
+ } else {
494
+ tenantResult.rows.forEach((row) =>
495
+ this.logger.log(
496
+ ` Table: ${row.tablename}, Rows: ${row.n_live_tup}`
497
+ )
328
498
  );
329
- this.logger.log(`Verification for schema '${this.migrationConfig.commonSchema}':`);
330
- publicResult.rows.forEach(row => this.logger.log(` Table: ${row.tablename}, Rows: ${row.n_live_tup}`));
499
+ }
331
500
  } catch (err) {
332
- this.logger.error(`Error verifying public schema: ${err.message}`);
333
- }
334
-
335
- // Verificar schemas de inquilinos (only if they were supposed to be created)
336
- let requiresTenantSchemas = Object.values(this.migrationConfig.tables).some(t => t.type === 'tenant');
337
- if (requiresTenantSchemas) {
338
- for (const tenantId of tenantIds) {
339
- try {
340
- const tenantResult = await this.connections.targetPool.query(
341
- `SELECT tablename, n_live_tup FROM pg_stat_user_tables WHERE schemaname = $1`,
342
- [tenantId]
343
- );
344
- this.logger.log(`Verification for tenant schema '${tenantId}':`);
345
- if (tenantResult.rows.length === 0) {
346
- this.logger.warn(` Schema ${tenantId} has no tables (or pg_stat_user_tables is empty).`);
347
- } else {
348
- tenantResult.rows.forEach(row => this.logger.log(` Table: ${row.tablename}, Rows: ${row.n_live_tup}`));
349
- }
350
- } catch (err) {
351
- this.logger.error(`Error verifying tenant schema ${tenantId}: ${err.message}`);
352
- }
353
- }
501
+ this.logger.error(
502
+ `Error verifying tenant schema ${tenantId}: ${err.message}`
503
+ );
354
504
  }
355
- this.logger.log("Verification finished.");
505
+ }
356
506
  }
507
+ this.logger.log("Verification finished.");
508
+ }
357
509
  }