@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.
- package/dist/scripts/data-migration/batch-migrator.d.ts +14 -19
- package/dist/scripts/data-migration/batch-migrator.js +98 -297
- package/dist/scripts/data-migration/batch-migrator.js.map +1 -1
- package/dist/scripts/data-migration/data-transformer.d.ts +16 -7
- package/dist/scripts/data-migration/data-transformer.js +169 -133
- package/dist/scripts/data-migration/data-transformer.js.map +1 -1
- package/dist/scripts/data-migration/db-connector.d.ts +6 -1
- package/dist/scripts/data-migration/db-connector.js +44 -8
- package/dist/scripts/data-migration/db-connector.js.map +1 -1
- package/dist/scripts/data-migration/dependency-resolver.d.ts +10 -10
- package/dist/scripts/data-migration/dependency-resolver.js +92 -211
- package/dist/scripts/data-migration/dependency-resolver.js.map +1 -1
- package/dist/scripts/data-migration/foreign-key-manager.d.ts +6 -5
- package/dist/scripts/data-migration/foreign-key-manager.js +108 -18
- package/dist/scripts/data-migration/foreign-key-manager.js.map +1 -1
- package/dist/scripts/data-migration/migration-config.json +63 -0
- package/dist/scripts/data-migration/migration-tool.d.ts +25 -6
- package/dist/scripts/data-migration/migration-tool.js +78 -38
- package/dist/scripts/data-migration/migration-tool.js.map +1 -1
- package/dist/scripts/data-migration/multi-source-migrator.d.ts +17 -0
- package/dist/scripts/data-migration/multi-source-migrator.js +130 -0
- package/dist/scripts/data-migration/multi-source-migrator.js.map +1 -0
- package/dist/scripts/data-migration/schema-utils.d.ts +3 -3
- package/dist/scripts/data-migration/schema-utils.js +62 -19
- package/dist/scripts/data-migration/schema-utils.js.map +1 -1
- package/dist/scripts/data-migration/tenant-migrator.js +9 -2
- package/dist/scripts/data-migration/tenant-migrator.js.map +1 -1
- package/dist/scripts/data-migration/typecast-manager.d.ts +7 -3
- package/dist/scripts/data-migration/typecast-manager.js +169 -25
- package/dist/scripts/data-migration/typecast-manager.js.map +1 -1
- package/dist/scripts/data-migration/types.d.ts +68 -2
- package/dist/scripts/database-initializer.d.ts +5 -0
- package/dist/scripts/database-initializer.js +45 -0
- package/dist/scripts/database-initializer.js.map +1 -0
- package/dist/scripts/fix-table-indexes.d.ts +26 -0
- package/dist/scripts/fix-table-indexes.js +460 -0
- package/dist/scripts/fix-table-indexes.js.map +1 -0
- package/dist/scripts/multi-db-migration.d.ts +1 -0
- package/dist/scripts/multi-db-migration.js +55 -0
- package/dist/scripts/multi-db-migration.js.map +1 -0
- package/dist/scripts/post-migration-validator.d.ts +18 -5
- package/dist/scripts/post-migration-validator.js +61 -39
- package/dist/scripts/post-migration-validator.js.map +1 -1
- package/dist/scripts/run-migration.js +83 -96
- package/dist/scripts/run-migration.js.map +1 -1
- package/dist/scripts/sequence-synchronizer.d.ts +8 -0
- package/dist/scripts/sequence-synchronizer.js +88 -0
- package/dist/scripts/sequence-synchronizer.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/migration-config.json +40 -72
- package/{migration-config-public.json → migration-config.json.bk} +14 -14
- package/package.json +10 -3
- package/prisma/migrations/{20250422001248_add_bank_account_id → 0_init}/migration.sql +269 -47
- package/prisma/migrations/20250505171324_add_bank_owner_name_in_manual_transfer/migration.sql +2 -0
- package/prisma/migrations/20250505173850_add_method_name_in_manual_transfer/migration.sql +2 -0
- package/prisma/migrations/migration_lock.toml +2 -2
- package/prisma/schema.prisma +214 -77
- package/src/scripts/data-migration/batch-migrator.ts +192 -513
- package/src/scripts/data-migration/data-transformer.ts +252 -203
- package/src/scripts/data-migration/db-connector.ts +66 -13
- package/src/scripts/data-migration/dependency-resolver.ts +121 -266
- package/src/scripts/data-migration/foreign-key-manager.ts +214 -32
- package/src/scripts/data-migration/migration-config.json +63 -0
- package/src/scripts/data-migration/migration-tool.ts +377 -225
- package/src/scripts/data-migration/schema-utils.ts +94 -32
- package/src/scripts/data-migration/tenant-migrator.ts +12 -5
- package/src/scripts/data-migration/typecast-manager.ts +186 -31
- package/src/scripts/data-migration/types.ts +78 -5
- package/src/scripts/database-initializer.ts +49 -0
- package/src/scripts/fix-table-indexes.ts +602 -0
- package/src/scripts/post-migration-validator.ts +206 -107
- package/src/scripts/run-migration.ts +140 -124
- package/src/scripts/sequence-synchronizer.ts +127 -0
- package/prisma/migrations/20250422001957_add_bank_account_id_relations/migration.sql +0 -14
- package/src/scripts/dumps/source_dump_20250413_112626.sql +0 -1527
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Logger } from "@nestjs/common";
|
|
2
|
-
import * as fs from
|
|
3
|
-
import * as path from
|
|
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;
|
|
15
|
-
targetSchema?: string;
|
|
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:
|
|
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
|
-
|
|
53
|
-
|
|
57
|
+
this.dataTransformer = new DataTransformer(
|
|
58
|
+
this.schemaUtils,
|
|
54
59
|
connections.sourcePool,
|
|
55
|
-
connections
|
|
60
|
+
connections
|
|
56
61
|
);
|
|
57
|
-
|
|
62
|
+
|
|
58
63
|
this.foreignKeyManager = new ForeignKeyManager(connections);
|
|
59
|
-
|
|
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.
|
|
68
|
-
|
|
69
|
-
|
|
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 =
|
|
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,
|
|
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(
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Error reading or parsing migration config file: ${error.message}`
|
|
93
|
+
);
|
|
84
94
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
113
|
-
|
|
128
|
+
const { commonSchema, tables: tablesConfig } = this.migrationConfig;
|
|
129
|
+
const allTablesInOrder =
|
|
130
|
+
await this.dependencyResolver.analyzeDependencies();
|
|
114
131
|
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
149
|
+
await this.connections.targetPool.query(
|
|
150
|
+
`SET session_replication_role = 'replica';`
|
|
151
|
+
);
|
|
125
152
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
147
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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();
|
|
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
|
-
|
|
205
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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(
|
|
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(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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(
|
|
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(
|
|
276
|
-
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
484
|
+
const tenantResult = await this.connections.targetPool.query(
|
|
326
485
|
`SELECT tablename, n_live_tup FROM pg_stat_user_tables WHERE schemaname = $1`,
|
|
327
|
-
[
|
|
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
|
-
|
|
330
|
-
publicResult.rows.forEach(row => this.logger.log(` Table: ${row.tablename}, Rows: ${row.n_live_tup}`));
|
|
499
|
+
}
|
|
331
500
|
} catch (err) {
|
|
332
|
-
|
|
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
|
-
|
|
505
|
+
}
|
|
356
506
|
}
|
|
507
|
+
this.logger.log("Verification finished.");
|
|
508
|
+
}
|
|
357
509
|
}
|