@javalabs/prisma-client 1.0.26 → 1.0.29
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/.github/CODEOWNERS +1 -1
- package/README.md +269 -269
- package/migration-config.json +63 -63
- package/migration-config.json.bk +95 -95
- package/migrations/add_reserved_amount.sql +7 -7
- package/package.json +44 -44
- package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
- package/prisma/schema.prisma +609 -607
- package/src/index.ts +23 -23
- package/src/prisma-factory.service.ts +40 -40
- package/src/prisma.module.ts +9 -9
- package/src/prisma.service.ts +16 -16
- package/src/scripts/add-uuid-to-table.ts +138 -138
- package/src/scripts/create-tenant-schemas.ts +145 -145
- package/src/scripts/data-migration/batch-migrator.ts +248 -248
- package/src/scripts/data-migration/data-transformer.ts +426 -426
- package/src/scripts/data-migration/db-connector.ts +120 -120
- package/src/scripts/data-migration/dependency-resolver.ts +174 -174
- package/src/scripts/data-migration/entity-discovery.ts +196 -196
- package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
- package/src/scripts/data-migration/migration-config.json +63 -63
- package/src/scripts/data-migration/migration-tool.ts +509 -509
- package/src/scripts/data-migration/schema-utils.ts +248 -248
- package/src/scripts/data-migration/tenant-migrator.ts +201 -201
- package/src/scripts/data-migration/typecast-manager.ts +193 -193
- package/src/scripts/data-migration/types.ts +113 -113
- package/src/scripts/database-initializer.ts +49 -49
- package/src/scripts/drop-database.ts +104 -104
- package/src/scripts/dump-source-db.sh +61 -61
- package/src/scripts/encrypt-user-passwords.ts +36 -36
- package/src/scripts/error-handler.ts +117 -117
- package/src/scripts/fix-data-types.ts +241 -241
- package/src/scripts/fix-enum-values.ts +357 -357
- package/src/scripts/fix-schema-discrepancies.ts +317 -317
- package/src/scripts/fix-table-indexes.ts +601 -601
- package/src/scripts/migrate-schema-structure.ts +90 -90
- package/src/scripts/migrate-uuid.ts +76 -76
- package/src/scripts/post-migration-validator.ts +526 -526
- package/src/scripts/pre-migration-validator.ts +610 -610
- package/src/scripts/reset-database.ts +263 -263
- package/src/scripts/retry-failed-migrations.ts +416 -416
- package/src/scripts/run-migration.ts +707 -707
- package/src/scripts/schema-sync.ts +128 -128
- package/src/scripts/sequence-sync-cli.ts +416 -416
- package/src/scripts/sequence-synchronizer.ts +127 -127
- package/src/scripts/sync-enum-types.ts +170 -170
- package/src/scripts/sync-enum-values.ts +563 -563
- package/src/scripts/truncate-database.ts +123 -123
- package/src/scripts/verify-migration-setup.ts +135 -135
- package/tsconfig.json +17 -17
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -34
- package/dist/index.js.map +0 -1
- package/dist/prisma-factory.service.d.ts +0 -9
- package/dist/prisma-factory.service.js +0 -47
- package/dist/prisma-factory.service.js.map +0 -1
- package/dist/prisma.module.d.ts +0 -2
- package/dist/prisma.module.js +0 -23
- package/dist/prisma.module.js.map +0 -1
- package/dist/prisma.service.d.ts +0 -6
- package/dist/prisma.service.js +0 -27
- package/dist/prisma.service.js.map +0 -1
- package/dist/scripts/add-uuid-to-table.d.ts +0 -8
- package/dist/scripts/add-uuid-to-table.js +0 -98
- package/dist/scripts/add-uuid-to-table.js.map +0 -1
- package/dist/scripts/create-tenant-schemas.d.ts +0 -1
- package/dist/scripts/create-tenant-schemas.js +0 -117
- package/dist/scripts/create-tenant-schemas.js.map +0 -1
- package/dist/scripts/data-migration/batch-migrator.d.ts +0 -20
- package/dist/scripts/data-migration/batch-migrator.js +0 -134
- package/dist/scripts/data-migration/batch-migrator.js.map +0 -1
- package/dist/scripts/data-migration/data-transformer.d.ts +0 -26
- package/dist/scripts/data-migration/data-transformer.js +0 -278
- package/dist/scripts/data-migration/data-transformer.js.map +0 -1
- package/dist/scripts/data-migration/db-connector.d.ts +0 -12
- package/dist/scripts/data-migration/db-connector.js +0 -94
- package/dist/scripts/data-migration/db-connector.js.map +0 -1
- package/dist/scripts/data-migration/dependency-resolver.d.ts +0 -18
- package/dist/scripts/data-migration/dependency-resolver.js +0 -132
- package/dist/scripts/data-migration/dependency-resolver.js.map +0 -1
- package/dist/scripts/data-migration/entity-discovery.d.ts +0 -11
- package/dist/scripts/data-migration/entity-discovery.js +0 -152
- package/dist/scripts/data-migration/entity-discovery.js.map +0 -1
- package/dist/scripts/data-migration/foreign-key-manager.d.ts +0 -18
- package/dist/scripts/data-migration/foreign-key-manager.js +0 -160
- package/dist/scripts/data-migration/foreign-key-manager.js.map +0 -1
- package/dist/scripts/data-migration/migration-tool.d.ts +0 -48
- package/dist/scripts/data-migration/migration-tool.js +0 -290
- package/dist/scripts/data-migration/migration-tool.js.map +0 -1
- package/dist/scripts/data-migration/schema-utils.d.ts +0 -18
- package/dist/scripts/data-migration/schema-utils.js +0 -207
- package/dist/scripts/data-migration/schema-utils.js.map +0 -1
- package/dist/scripts/data-migration/tenant-migrator.d.ts +0 -15
- package/dist/scripts/data-migration/tenant-migrator.js +0 -117
- package/dist/scripts/data-migration/tenant-migrator.js.map +0 -1
- package/dist/scripts/data-migration/typecast-manager.d.ts +0 -9
- package/dist/scripts/data-migration/typecast-manager.js +0 -179
- package/dist/scripts/data-migration/typecast-manager.js.map +0 -1
- package/dist/scripts/data-migration/types.d.ts +0 -100
- package/dist/scripts/data-migration/types.js +0 -3
- package/dist/scripts/data-migration/types.js.map +0 -1
- package/dist/scripts/database-initializer.d.ts +0 -5
- package/dist/scripts/database-initializer.js +0 -45
- package/dist/scripts/database-initializer.js.map +0 -1
- package/dist/scripts/drop-database.d.ts +0 -10
- package/dist/scripts/drop-database.js +0 -81
- package/dist/scripts/drop-database.js.map +0 -1
- package/dist/scripts/encrypt-user-passwords.d.ts +0 -1
- package/dist/scripts/encrypt-user-passwords.js +0 -33
- package/dist/scripts/encrypt-user-passwords.js.map +0 -1
- package/dist/scripts/error-handler.d.ts +0 -12
- package/dist/scripts/error-handler.js +0 -82
- package/dist/scripts/error-handler.js.map +0 -1
- package/dist/scripts/fix-data-types.d.ts +0 -10
- package/dist/scripts/fix-data-types.js +0 -185
- package/dist/scripts/fix-data-types.js.map +0 -1
- package/dist/scripts/fix-enum-values.d.ts +0 -17
- package/dist/scripts/fix-enum-values.js +0 -234
- package/dist/scripts/fix-enum-values.js.map +0 -1
- package/dist/scripts/fix-schema-discrepancies.d.ts +0 -21
- package/dist/scripts/fix-schema-discrepancies.js +0 -240
- package/dist/scripts/fix-schema-discrepancies.js.map +0 -1
- package/dist/scripts/fix-table-indexes.d.ts +0 -26
- package/dist/scripts/fix-table-indexes.js +0 -460
- package/dist/scripts/fix-table-indexes.js.map +0 -1
- package/dist/scripts/migrate-schema-structure.d.ts +0 -1
- package/dist/scripts/migrate-schema-structure.js +0 -76
- package/dist/scripts/migrate-schema-structure.js.map +0 -1
- package/dist/scripts/migrate-uuid.d.ts +0 -2
- package/dist/scripts/migrate-uuid.js +0 -57
- package/dist/scripts/migrate-uuid.js.map +0 -1
- package/dist/scripts/post-migration-validator.d.ts +0 -34
- package/dist/scripts/post-migration-validator.js +0 -363
- package/dist/scripts/post-migration-validator.js.map +0 -1
- package/dist/scripts/pre-migration-validator.d.ts +0 -25
- package/dist/scripts/pre-migration-validator.js +0 -491
- package/dist/scripts/pre-migration-validator.js.map +0 -1
- package/dist/scripts/reset-database.d.ts +0 -17
- package/dist/scripts/reset-database.js +0 -202
- package/dist/scripts/reset-database.js.map +0 -1
- package/dist/scripts/retry-failed-migrations.d.ts +0 -14
- package/dist/scripts/retry-failed-migrations.js +0 -301
- package/dist/scripts/retry-failed-migrations.js.map +0 -1
- package/dist/scripts/run-migration.d.ts +0 -1
- package/dist/scripts/run-migration.js +0 -512
- package/dist/scripts/run-migration.js.map +0 -1
- package/dist/scripts/schema-sync.d.ts +0 -1
- package/dist/scripts/schema-sync.js +0 -85
- package/dist/scripts/schema-sync.js.map +0 -1
- package/dist/scripts/sequence-sync-cli.d.ts +0 -2
- package/dist/scripts/sequence-sync-cli.js +0 -287
- package/dist/scripts/sequence-sync-cli.js.map +0 -1
- package/dist/scripts/sequence-synchronizer.d.ts +0 -8
- package/dist/scripts/sequence-synchronizer.js +0 -88
- package/dist/scripts/sequence-synchronizer.js.map +0 -1
- package/dist/scripts/sync-enum-types.d.ts +0 -13
- package/dist/scripts/sync-enum-types.js +0 -139
- package/dist/scripts/sync-enum-types.js.map +0 -1
- package/dist/scripts/sync-enum-values.d.ts +0 -20
- package/dist/scripts/sync-enum-values.js +0 -336
- package/dist/scripts/sync-enum-values.js.map +0 -1
- package/dist/scripts/truncate-database.d.ts +0 -10
- package/dist/scripts/truncate-database.js +0 -100
- package/dist/scripts/truncate-database.js.map +0 -1
- package/dist/scripts/verify-migration-setup.d.ts +0 -11
- package/dist/scripts/verify-migration-setup.js +0 -120
- package/dist/scripts/verify-migration-setup.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/prisma/migrations/add_athena_match_fields.sql +0 -18
- package/prisma/migrations/add_bank_receipt_number.sql +0 -9
|
@@ -1,426 +1,426 @@
|
|
|
1
|
-
import { Logger } from "@nestjs/common";
|
|
2
|
-
import { ColumnSchema, EnumCastValue } from "./types";
|
|
3
|
-
import { SchemaUtils } from "./schema-utils";
|
|
4
|
-
import {
|
|
5
|
-
DatabaseConnections,
|
|
6
|
-
MigrationConfig,
|
|
7
|
-
MigrationOptions,
|
|
8
|
-
} from "./types";
|
|
9
|
-
import { ForeignKeyManager } from "./foreign-key-manager";
|
|
10
|
-
import { TypecastManager } from "./typecast-manager";
|
|
11
|
-
|
|
12
|
-
export class DataTransformer {
|
|
13
|
-
private readonly logger = new Logger("DataTransformer");
|
|
14
|
-
private readonly schemaUtils: SchemaUtils;
|
|
15
|
-
private readonly sourceConnection: any;
|
|
16
|
-
private connections: DatabaseConnections;
|
|
17
|
-
private migrationConfig: MigrationConfig;
|
|
18
|
-
private options: MigrationOptions;
|
|
19
|
-
private foreignKeyManager: ForeignKeyManager;
|
|
20
|
-
private readonly typecastManager: TypecastManager;
|
|
21
|
-
|
|
22
|
-
constructor(
|
|
23
|
-
schemaUtils: SchemaUtils,
|
|
24
|
-
sourceConnection: any,
|
|
25
|
-
connections: DatabaseConnections
|
|
26
|
-
) {
|
|
27
|
-
this.schemaUtils = schemaUtils;
|
|
28
|
-
this.sourceConnection = sourceConnection;
|
|
29
|
-
this.connections = connections;
|
|
30
|
-
this.typecastManager = new TypecastManager();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async validateEnumValue(
|
|
34
|
-
tableName: string,
|
|
35
|
-
columnName: string,
|
|
36
|
-
value: string | null,
|
|
37
|
-
isNullable: boolean = true
|
|
38
|
-
): Promise<string | null> {
|
|
39
|
-
try {
|
|
40
|
-
if (value === null) {
|
|
41
|
-
return isNullable
|
|
42
|
-
? null
|
|
43
|
-
: await this.getDefaultEnumValue(tableName, columnName);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const enumTypeName = await this.getEnumTypeName(tableName, columnName);
|
|
47
|
-
const normalizedValue = value.toString().trim().toLowerCase();
|
|
48
|
-
|
|
49
|
-
// Obtener valores válidos del enum desde la base de datos
|
|
50
|
-
const query = `SELECT unnest(enum_range(NULL::${enumTypeName})) as enum_value`;
|
|
51
|
-
const validValues = await this.connections.sourcePool.query(query);
|
|
52
|
-
|
|
53
|
-
if (!validValues || !validValues.rows) {
|
|
54
|
-
this.logger.warn(
|
|
55
|
-
`No se pudieron obtener valores válidos para el enum ${enumTypeName}`
|
|
56
|
-
);
|
|
57
|
-
return await this.getDefaultEnumValue(enumTypeName);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const enumValues = validValues.rows.map((row: any) =>
|
|
61
|
-
row.enum_value.toLowerCase()
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// Verificar coincidencia exacta
|
|
65
|
-
if (enumValues.includes(normalizedValue)) {
|
|
66
|
-
return value.toString().trim();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Verificar coincidencia parcial
|
|
70
|
-
const partialMatch = enumValues.find(
|
|
71
|
-
(enumValue) =>
|
|
72
|
-
enumValue.includes(normalizedValue) ||
|
|
73
|
-
normalizedValue.includes(enumValue)
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
if (partialMatch) {
|
|
77
|
-
this.logger.debug(
|
|
78
|
-
`Valor enum corregido: ${value} -> ${partialMatch} para ${tableName}.${columnName}`
|
|
79
|
-
);
|
|
80
|
-
return partialMatch;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Si no se encuentra coincidencia, usar valor por defecto
|
|
84
|
-
const defaultValue = await this.getDefaultEnumValue(enumTypeName);
|
|
85
|
-
if (defaultValue) {
|
|
86
|
-
this.logger.warn(
|
|
87
|
-
`Usando valor por defecto ${defaultValue} para ${tableName}.${columnName}`
|
|
88
|
-
);
|
|
89
|
-
return defaultValue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (!isNullable) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`No se pudo encontrar un valor válido para el enum ${enumTypeName}`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return null;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
this.logger.error(
|
|
101
|
-
`Error validating enum value for ${tableName}.${columnName}: ${error.message}`
|
|
102
|
-
);
|
|
103
|
-
if (!isNullable) {
|
|
104
|
-
return await this.getDefaultEnumValue(tableName, columnName);
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
private async getEnumTypeName(
|
|
111
|
-
tableName: string,
|
|
112
|
-
columnName: string
|
|
113
|
-
): Promise<string> {
|
|
114
|
-
const result = await this.connections.sourcePool.query(
|
|
115
|
-
`
|
|
116
|
-
SELECT udt_name
|
|
117
|
-
FROM information_schema.columns
|
|
118
|
-
WHERE table_name = $1
|
|
119
|
-
AND column_name = $2
|
|
120
|
-
AND table_schema = 'public'
|
|
121
|
-
`,
|
|
122
|
-
[tableName.replace(/^public\./, ""), columnName]
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
if (result.rows.length === 0) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
`No se encontró el tipo de enum para ${tableName}.${columnName}`
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return result.rows[0].udt_name;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
transformToNumeric(value: any): number | null {
|
|
135
|
-
if (value === null || value === "") {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const numericValue = parseFloat(value);
|
|
140
|
-
if (isNaN(numericValue)) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return numericValue;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private async getDefaultEnumValue(
|
|
148
|
-
enumType: string,
|
|
149
|
-
tenantId: string = "public"
|
|
150
|
-
): Promise<string | null> {
|
|
151
|
-
try {
|
|
152
|
-
// Mapeo específico para tipos de enum conocidos
|
|
153
|
-
const enumDefaults = {
|
|
154
|
-
enum_users_role: "user",
|
|
155
|
-
enum_account_type: "savings",
|
|
156
|
-
enum_transaction_type: "payin",
|
|
157
|
-
enum_transaction_status: "pending",
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (enumDefaults[enumType]) {
|
|
161
|
-
return enumDefaults[enumType];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Si no hay un valor por defecto específico, obtener el primer valor del enum
|
|
165
|
-
const query = `
|
|
166
|
-
SELECT e.enumlabel
|
|
167
|
-
FROM pg_type t
|
|
168
|
-
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
169
|
-
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
170
|
-
WHERE t.typname = $1 AND n.nspname = $2
|
|
171
|
-
ORDER BY e.enumsortorder
|
|
172
|
-
LIMIT 1
|
|
173
|
-
`;
|
|
174
|
-
|
|
175
|
-
const result = await this.schemaUtils.queryTargetDb(query, [
|
|
176
|
-
enumType,
|
|
177
|
-
tenantId,
|
|
178
|
-
]);
|
|
179
|
-
|
|
180
|
-
if (result.rows.length > 0) {
|
|
181
|
-
return result.rows[0].enumlabel;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return null;
|
|
185
|
-
} catch (error) {
|
|
186
|
-
this.logger.error(
|
|
187
|
-
`Error getting default enum value for ${enumType}: ${error.message}`
|
|
188
|
-
);
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async prepareEnumValue(
|
|
194
|
-
tenantId: string,
|
|
195
|
-
enumType: string,
|
|
196
|
-
value: string
|
|
197
|
-
): Promise<EnumCastValue | null> {
|
|
198
|
-
if (value === null || value === undefined || value === "") {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const validatedValue = await this.validateEnumValue(
|
|
203
|
-
tenantId,
|
|
204
|
-
enumType,
|
|
205
|
-
value
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
if (validatedValue === null) {
|
|
209
|
-
// Obtener valor por defecto dinámicamente
|
|
210
|
-
const defaultValue = await this.getDefaultEnumValue(enumType, tenantId);
|
|
211
|
-
if (defaultValue) {
|
|
212
|
-
this.logger.warn(
|
|
213
|
-
`Using first enum value '${defaultValue}' as default for invalid value '${value}' of type ${enumType}`
|
|
214
|
-
);
|
|
215
|
-
return {
|
|
216
|
-
needsEnumCast: true,
|
|
217
|
-
value: defaultValue,
|
|
218
|
-
enumType,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
needsEnumCast: true,
|
|
226
|
-
value: validatedValue,
|
|
227
|
-
enumType,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async transformColumnValue(
|
|
232
|
-
value: any,
|
|
233
|
-
columnName: string,
|
|
234
|
-
targetColumn: ColumnSchema & { source_type?: string },
|
|
235
|
-
tenantId: string
|
|
236
|
-
): Promise<any> {
|
|
237
|
-
if (value === null || value === undefined) {
|
|
238
|
-
return targetColumn.is_nullable === "YES"
|
|
239
|
-
? null
|
|
240
|
-
: this.getDefaultValue(targetColumn);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
try {
|
|
244
|
-
// Si tenemos el tipo de origen, usar el TypecastManager
|
|
245
|
-
if (targetColumn.source_type) {
|
|
246
|
-
return this.typecastManager.castValue(
|
|
247
|
-
value,
|
|
248
|
-
targetColumn.source_type,
|
|
249
|
-
targetColumn.data_type
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Si no tenemos el tipo de origen, intentar convertir basado en el tipo destino
|
|
254
|
-
switch (targetColumn.data_type.toLowerCase()) {
|
|
255
|
-
case "integer":
|
|
256
|
-
case "bigint":
|
|
257
|
-
case "smallint":
|
|
258
|
-
return typeof value === "string" ? parseInt(value, 10) : value;
|
|
259
|
-
case "numeric":
|
|
260
|
-
case "decimal":
|
|
261
|
-
case "real":
|
|
262
|
-
case "double precision":
|
|
263
|
-
return typeof value === "string" ? parseFloat(value) : value;
|
|
264
|
-
case "boolean":
|
|
265
|
-
if (typeof value === "string") {
|
|
266
|
-
return value.toLowerCase() === "true" || value === "1";
|
|
267
|
-
}
|
|
268
|
-
return Boolean(value);
|
|
269
|
-
case "json":
|
|
270
|
-
case "jsonb":
|
|
271
|
-
if (typeof value === "string") {
|
|
272
|
-
try {
|
|
273
|
-
return JSON.parse(value);
|
|
274
|
-
} catch {
|
|
275
|
-
return value;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return value;
|
|
279
|
-
case "timestamp":
|
|
280
|
-
case "timestamptz":
|
|
281
|
-
case "timestamp with time zone":
|
|
282
|
-
case "timestamp without time zone":
|
|
283
|
-
if (value instanceof Date) {
|
|
284
|
-
return value.toISOString();
|
|
285
|
-
}
|
|
286
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
287
|
-
return new Date(value).toISOString();
|
|
288
|
-
}
|
|
289
|
-
return value;
|
|
290
|
-
case "date":
|
|
291
|
-
if (value instanceof Date) {
|
|
292
|
-
return value.toISOString().split("T")[0];
|
|
293
|
-
}
|
|
294
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
295
|
-
return new Date(value).toISOString().split("T")[0];
|
|
296
|
-
}
|
|
297
|
-
return value;
|
|
298
|
-
case "text":
|
|
299
|
-
case "character varying":
|
|
300
|
-
case "varchar":
|
|
301
|
-
case "char":
|
|
302
|
-
return String(value);
|
|
303
|
-
default:
|
|
304
|
-
return value;
|
|
305
|
-
}
|
|
306
|
-
} catch (error) {
|
|
307
|
-
this.logger.warn(
|
|
308
|
-
`Error transformando valor para columna ${columnName}: ${error.message}`
|
|
309
|
-
);
|
|
310
|
-
return targetColumn.is_nullable === "YES"
|
|
311
|
-
? null
|
|
312
|
-
: this.getDefaultValue(targetColumn);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
private getDefaultValue(column: ColumnSchema): any {
|
|
317
|
-
switch (column.data_type.toLowerCase()) {
|
|
318
|
-
case "integer":
|
|
319
|
-
case "bigint":
|
|
320
|
-
case "smallint":
|
|
321
|
-
return 0;
|
|
322
|
-
case "numeric":
|
|
323
|
-
case "decimal":
|
|
324
|
-
case "real":
|
|
325
|
-
case "double precision":
|
|
326
|
-
return 0.0;
|
|
327
|
-
case "boolean":
|
|
328
|
-
return false;
|
|
329
|
-
case "text":
|
|
330
|
-
case "character varying":
|
|
331
|
-
case "varchar":
|
|
332
|
-
case "char":
|
|
333
|
-
return "";
|
|
334
|
-
case "json":
|
|
335
|
-
case "jsonb":
|
|
336
|
-
return "{}";
|
|
337
|
-
case "timestamp":
|
|
338
|
-
case "timestamptz":
|
|
339
|
-
case "timestamp with time zone":
|
|
340
|
-
case "timestamp without time zone":
|
|
341
|
-
return new Date().toISOString();
|
|
342
|
-
case "date":
|
|
343
|
-
return new Date().toISOString().split("T")[0];
|
|
344
|
-
default:
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
private extractEnumValueFromObject(value: any): string | null {
|
|
350
|
-
if (typeof value === "object" && value !== null) {
|
|
351
|
-
// Intentar extraer el valor de propiedades comunes que podrían contener el valor enum
|
|
352
|
-
const possibleProperties = ["value", "type", "name", "code"];
|
|
353
|
-
for (const prop of possibleProperties) {
|
|
354
|
-
if (value[prop] && typeof value[prop] === "string") {
|
|
355
|
-
return value[prop].trim();
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
private async handleEnumType(
|
|
363
|
-
value: any,
|
|
364
|
-
columnName: string,
|
|
365
|
-
targetColumn: ColumnSchema,
|
|
366
|
-
tenantId: string
|
|
367
|
-
): Promise<string | null> {
|
|
368
|
-
// Si el valor es null y la columna permite null
|
|
369
|
-
if (value === null && targetColumn.is_nullable === "YES") {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Obtener el tipo específico de enum
|
|
374
|
-
const enumType = targetColumn.udt_name;
|
|
375
|
-
|
|
376
|
-
// Validar el valor según el tipo específico de enum
|
|
377
|
-
const validatedValue = await this.validateEnumValue(
|
|
378
|
-
tenantId,
|
|
379
|
-
columnName,
|
|
380
|
-
value,
|
|
381
|
-
targetColumn.is_nullable === "YES"
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
return validatedValue;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
async transformRecord(
|
|
388
|
-
record: any,
|
|
389
|
-
sourceSchema: ColumnSchema[],
|
|
390
|
-
targetSchema: ColumnSchema[],
|
|
391
|
-
tenantId: string
|
|
392
|
-
): Promise<any> {
|
|
393
|
-
const transformedData: any = {};
|
|
394
|
-
|
|
395
|
-
for (const [key, value] of Object.entries(record)) {
|
|
396
|
-
// Primero intentar encontrar una coincidencia exacta
|
|
397
|
-
let targetColumn = targetSchema.find((col) => col.column_name === key);
|
|
398
|
-
|
|
399
|
-
// Si no se encuentra coincidencia exacta, intentar case-insensitive
|
|
400
|
-
if (!targetColumn) {
|
|
401
|
-
targetColumn = targetSchema.find(
|
|
402
|
-
(col) => col.column_name.toLowerCase() === key.toLowerCase()
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (!targetColumn) {
|
|
407
|
-
this.logger.warn(`Column ${key} not found in target schema, skipping`);
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Usar el nombre exacto de la columna del schema destino
|
|
412
|
-
const columnName = targetColumn.column_name;
|
|
413
|
-
|
|
414
|
-
const transformedValue = await this.transformColumnValue(
|
|
415
|
-
value,
|
|
416
|
-
columnName,
|
|
417
|
-
targetColumn,
|
|
418
|
-
tenantId
|
|
419
|
-
);
|
|
420
|
-
|
|
421
|
-
transformedData[columnName] = transformedValue;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
return transformedData;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
import { ColumnSchema, EnumCastValue } from "./types";
|
|
3
|
+
import { SchemaUtils } from "./schema-utils";
|
|
4
|
+
import {
|
|
5
|
+
DatabaseConnections,
|
|
6
|
+
MigrationConfig,
|
|
7
|
+
MigrationOptions,
|
|
8
|
+
} from "./types";
|
|
9
|
+
import { ForeignKeyManager } from "./foreign-key-manager";
|
|
10
|
+
import { TypecastManager } from "./typecast-manager";
|
|
11
|
+
|
|
12
|
+
export class DataTransformer {
|
|
13
|
+
private readonly logger = new Logger("DataTransformer");
|
|
14
|
+
private readonly schemaUtils: SchemaUtils;
|
|
15
|
+
private readonly sourceConnection: any;
|
|
16
|
+
private connections: DatabaseConnections;
|
|
17
|
+
private migrationConfig: MigrationConfig;
|
|
18
|
+
private options: MigrationOptions;
|
|
19
|
+
private foreignKeyManager: ForeignKeyManager;
|
|
20
|
+
private readonly typecastManager: TypecastManager;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
schemaUtils: SchemaUtils,
|
|
24
|
+
sourceConnection: any,
|
|
25
|
+
connections: DatabaseConnections
|
|
26
|
+
) {
|
|
27
|
+
this.schemaUtils = schemaUtils;
|
|
28
|
+
this.sourceConnection = sourceConnection;
|
|
29
|
+
this.connections = connections;
|
|
30
|
+
this.typecastManager = new TypecastManager();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async validateEnumValue(
|
|
34
|
+
tableName: string,
|
|
35
|
+
columnName: string,
|
|
36
|
+
value: string | null,
|
|
37
|
+
isNullable: boolean = true
|
|
38
|
+
): Promise<string | null> {
|
|
39
|
+
try {
|
|
40
|
+
if (value === null) {
|
|
41
|
+
return isNullable
|
|
42
|
+
? null
|
|
43
|
+
: await this.getDefaultEnumValue(tableName, columnName);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const enumTypeName = await this.getEnumTypeName(tableName, columnName);
|
|
47
|
+
const normalizedValue = value.toString().trim().toLowerCase();
|
|
48
|
+
|
|
49
|
+
// Obtener valores válidos del enum desde la base de datos
|
|
50
|
+
const query = `SELECT unnest(enum_range(NULL::${enumTypeName})) as enum_value`;
|
|
51
|
+
const validValues = await this.connections.sourcePool.query(query);
|
|
52
|
+
|
|
53
|
+
if (!validValues || !validValues.rows) {
|
|
54
|
+
this.logger.warn(
|
|
55
|
+
`No se pudieron obtener valores válidos para el enum ${enumTypeName}`
|
|
56
|
+
);
|
|
57
|
+
return await this.getDefaultEnumValue(enumTypeName);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const enumValues = validValues.rows.map((row: any) =>
|
|
61
|
+
row.enum_value.toLowerCase()
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Verificar coincidencia exacta
|
|
65
|
+
if (enumValues.includes(normalizedValue)) {
|
|
66
|
+
return value.toString().trim();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Verificar coincidencia parcial
|
|
70
|
+
const partialMatch = enumValues.find(
|
|
71
|
+
(enumValue) =>
|
|
72
|
+
enumValue.includes(normalizedValue) ||
|
|
73
|
+
normalizedValue.includes(enumValue)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (partialMatch) {
|
|
77
|
+
this.logger.debug(
|
|
78
|
+
`Valor enum corregido: ${value} -> ${partialMatch} para ${tableName}.${columnName}`
|
|
79
|
+
);
|
|
80
|
+
return partialMatch;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Si no se encuentra coincidencia, usar valor por defecto
|
|
84
|
+
const defaultValue = await this.getDefaultEnumValue(enumTypeName);
|
|
85
|
+
if (defaultValue) {
|
|
86
|
+
this.logger.warn(
|
|
87
|
+
`Usando valor por defecto ${defaultValue} para ${tableName}.${columnName}`
|
|
88
|
+
);
|
|
89
|
+
return defaultValue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!isNullable) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`No se pudo encontrar un valor válido para el enum ${enumTypeName}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.logger.error(
|
|
101
|
+
`Error validating enum value for ${tableName}.${columnName}: ${error.message}`
|
|
102
|
+
);
|
|
103
|
+
if (!isNullable) {
|
|
104
|
+
return await this.getDefaultEnumValue(tableName, columnName);
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async getEnumTypeName(
|
|
111
|
+
tableName: string,
|
|
112
|
+
columnName: string
|
|
113
|
+
): Promise<string> {
|
|
114
|
+
const result = await this.connections.sourcePool.query(
|
|
115
|
+
`
|
|
116
|
+
SELECT udt_name
|
|
117
|
+
FROM information_schema.columns
|
|
118
|
+
WHERE table_name = $1
|
|
119
|
+
AND column_name = $2
|
|
120
|
+
AND table_schema = 'public'
|
|
121
|
+
`,
|
|
122
|
+
[tableName.replace(/^public\./, ""), columnName]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (result.rows.length === 0) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`No se encontró el tipo de enum para ${tableName}.${columnName}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return result.rows[0].udt_name;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
transformToNumeric(value: any): number | null {
|
|
135
|
+
if (value === null || value === "") {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const numericValue = parseFloat(value);
|
|
140
|
+
if (isNaN(numericValue)) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return numericValue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async getDefaultEnumValue(
|
|
148
|
+
enumType: string,
|
|
149
|
+
tenantId: string = "public"
|
|
150
|
+
): Promise<string | null> {
|
|
151
|
+
try {
|
|
152
|
+
// Mapeo específico para tipos de enum conocidos
|
|
153
|
+
const enumDefaults = {
|
|
154
|
+
enum_users_role: "user",
|
|
155
|
+
enum_account_type: "savings",
|
|
156
|
+
enum_transaction_type: "payin",
|
|
157
|
+
enum_transaction_status: "pending",
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (enumDefaults[enumType]) {
|
|
161
|
+
return enumDefaults[enumType];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Si no hay un valor por defecto específico, obtener el primer valor del enum
|
|
165
|
+
const query = `
|
|
166
|
+
SELECT e.enumlabel
|
|
167
|
+
FROM pg_type t
|
|
168
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
169
|
+
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
170
|
+
WHERE t.typname = $1 AND n.nspname = $2
|
|
171
|
+
ORDER BY e.enumsortorder
|
|
172
|
+
LIMIT 1
|
|
173
|
+
`;
|
|
174
|
+
|
|
175
|
+
const result = await this.schemaUtils.queryTargetDb(query, [
|
|
176
|
+
enumType,
|
|
177
|
+
tenantId,
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
if (result.rows.length > 0) {
|
|
181
|
+
return result.rows[0].enumlabel;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return null;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
this.logger.error(
|
|
187
|
+
`Error getting default enum value for ${enumType}: ${error.message}`
|
|
188
|
+
);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async prepareEnumValue(
|
|
194
|
+
tenantId: string,
|
|
195
|
+
enumType: string,
|
|
196
|
+
value: string
|
|
197
|
+
): Promise<EnumCastValue | null> {
|
|
198
|
+
if (value === null || value === undefined || value === "") {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const validatedValue = await this.validateEnumValue(
|
|
203
|
+
tenantId,
|
|
204
|
+
enumType,
|
|
205
|
+
value
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (validatedValue === null) {
|
|
209
|
+
// Obtener valor por defecto dinámicamente
|
|
210
|
+
const defaultValue = await this.getDefaultEnumValue(enumType, tenantId);
|
|
211
|
+
if (defaultValue) {
|
|
212
|
+
this.logger.warn(
|
|
213
|
+
`Using first enum value '${defaultValue}' as default for invalid value '${value}' of type ${enumType}`
|
|
214
|
+
);
|
|
215
|
+
return {
|
|
216
|
+
needsEnumCast: true,
|
|
217
|
+
value: defaultValue,
|
|
218
|
+
enumType,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
needsEnumCast: true,
|
|
226
|
+
value: validatedValue,
|
|
227
|
+
enumType,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async transformColumnValue(
|
|
232
|
+
value: any,
|
|
233
|
+
columnName: string,
|
|
234
|
+
targetColumn: ColumnSchema & { source_type?: string },
|
|
235
|
+
tenantId: string
|
|
236
|
+
): Promise<any> {
|
|
237
|
+
if (value === null || value === undefined) {
|
|
238
|
+
return targetColumn.is_nullable === "YES"
|
|
239
|
+
? null
|
|
240
|
+
: this.getDefaultValue(targetColumn);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
// Si tenemos el tipo de origen, usar el TypecastManager
|
|
245
|
+
if (targetColumn.source_type) {
|
|
246
|
+
return this.typecastManager.castValue(
|
|
247
|
+
value,
|
|
248
|
+
targetColumn.source_type,
|
|
249
|
+
targetColumn.data_type
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Si no tenemos el tipo de origen, intentar convertir basado en el tipo destino
|
|
254
|
+
switch (targetColumn.data_type.toLowerCase()) {
|
|
255
|
+
case "integer":
|
|
256
|
+
case "bigint":
|
|
257
|
+
case "smallint":
|
|
258
|
+
return typeof value === "string" ? parseInt(value, 10) : value;
|
|
259
|
+
case "numeric":
|
|
260
|
+
case "decimal":
|
|
261
|
+
case "real":
|
|
262
|
+
case "double precision":
|
|
263
|
+
return typeof value === "string" ? parseFloat(value) : value;
|
|
264
|
+
case "boolean":
|
|
265
|
+
if (typeof value === "string") {
|
|
266
|
+
return value.toLowerCase() === "true" || value === "1";
|
|
267
|
+
}
|
|
268
|
+
return Boolean(value);
|
|
269
|
+
case "json":
|
|
270
|
+
case "jsonb":
|
|
271
|
+
if (typeof value === "string") {
|
|
272
|
+
try {
|
|
273
|
+
return JSON.parse(value);
|
|
274
|
+
} catch {
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return value;
|
|
279
|
+
case "timestamp":
|
|
280
|
+
case "timestamptz":
|
|
281
|
+
case "timestamp with time zone":
|
|
282
|
+
case "timestamp without time zone":
|
|
283
|
+
if (value instanceof Date) {
|
|
284
|
+
return value.toISOString();
|
|
285
|
+
}
|
|
286
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
287
|
+
return new Date(value).toISOString();
|
|
288
|
+
}
|
|
289
|
+
return value;
|
|
290
|
+
case "date":
|
|
291
|
+
if (value instanceof Date) {
|
|
292
|
+
return value.toISOString().split("T")[0];
|
|
293
|
+
}
|
|
294
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
295
|
+
return new Date(value).toISOString().split("T")[0];
|
|
296
|
+
}
|
|
297
|
+
return value;
|
|
298
|
+
case "text":
|
|
299
|
+
case "character varying":
|
|
300
|
+
case "varchar":
|
|
301
|
+
case "char":
|
|
302
|
+
return String(value);
|
|
303
|
+
default:
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
this.logger.warn(
|
|
308
|
+
`Error transformando valor para columna ${columnName}: ${error.message}`
|
|
309
|
+
);
|
|
310
|
+
return targetColumn.is_nullable === "YES"
|
|
311
|
+
? null
|
|
312
|
+
: this.getDefaultValue(targetColumn);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private getDefaultValue(column: ColumnSchema): any {
|
|
317
|
+
switch (column.data_type.toLowerCase()) {
|
|
318
|
+
case "integer":
|
|
319
|
+
case "bigint":
|
|
320
|
+
case "smallint":
|
|
321
|
+
return 0;
|
|
322
|
+
case "numeric":
|
|
323
|
+
case "decimal":
|
|
324
|
+
case "real":
|
|
325
|
+
case "double precision":
|
|
326
|
+
return 0.0;
|
|
327
|
+
case "boolean":
|
|
328
|
+
return false;
|
|
329
|
+
case "text":
|
|
330
|
+
case "character varying":
|
|
331
|
+
case "varchar":
|
|
332
|
+
case "char":
|
|
333
|
+
return "";
|
|
334
|
+
case "json":
|
|
335
|
+
case "jsonb":
|
|
336
|
+
return "{}";
|
|
337
|
+
case "timestamp":
|
|
338
|
+
case "timestamptz":
|
|
339
|
+
case "timestamp with time zone":
|
|
340
|
+
case "timestamp without time zone":
|
|
341
|
+
return new Date().toISOString();
|
|
342
|
+
case "date":
|
|
343
|
+
return new Date().toISOString().split("T")[0];
|
|
344
|
+
default:
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private extractEnumValueFromObject(value: any): string | null {
|
|
350
|
+
if (typeof value === "object" && value !== null) {
|
|
351
|
+
// Intentar extraer el valor de propiedades comunes que podrían contener el valor enum
|
|
352
|
+
const possibleProperties = ["value", "type", "name", "code"];
|
|
353
|
+
for (const prop of possibleProperties) {
|
|
354
|
+
if (value[prop] && typeof value[prop] === "string") {
|
|
355
|
+
return value[prop].trim();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private async handleEnumType(
|
|
363
|
+
value: any,
|
|
364
|
+
columnName: string,
|
|
365
|
+
targetColumn: ColumnSchema,
|
|
366
|
+
tenantId: string
|
|
367
|
+
): Promise<string | null> {
|
|
368
|
+
// Si el valor es null y la columna permite null
|
|
369
|
+
if (value === null && targetColumn.is_nullable === "YES") {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Obtener el tipo específico de enum
|
|
374
|
+
const enumType = targetColumn.udt_name;
|
|
375
|
+
|
|
376
|
+
// Validar el valor según el tipo específico de enum
|
|
377
|
+
const validatedValue = await this.validateEnumValue(
|
|
378
|
+
tenantId,
|
|
379
|
+
columnName,
|
|
380
|
+
value,
|
|
381
|
+
targetColumn.is_nullable === "YES"
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
return validatedValue;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async transformRecord(
|
|
388
|
+
record: any,
|
|
389
|
+
sourceSchema: ColumnSchema[],
|
|
390
|
+
targetSchema: ColumnSchema[],
|
|
391
|
+
tenantId: string
|
|
392
|
+
): Promise<any> {
|
|
393
|
+
const transformedData: any = {};
|
|
394
|
+
|
|
395
|
+
for (const [key, value] of Object.entries(record)) {
|
|
396
|
+
// Primero intentar encontrar una coincidencia exacta
|
|
397
|
+
let targetColumn = targetSchema.find((col) => col.column_name === key);
|
|
398
|
+
|
|
399
|
+
// Si no se encuentra coincidencia exacta, intentar case-insensitive
|
|
400
|
+
if (!targetColumn) {
|
|
401
|
+
targetColumn = targetSchema.find(
|
|
402
|
+
(col) => col.column_name.toLowerCase() === key.toLowerCase()
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (!targetColumn) {
|
|
407
|
+
this.logger.warn(`Column ${key} not found in target schema, skipping`);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Usar el nombre exacto de la columna del schema destino
|
|
412
|
+
const columnName = targetColumn.column_name;
|
|
413
|
+
|
|
414
|
+
const transformedValue = await this.transformColumnValue(
|
|
415
|
+
value,
|
|
416
|
+
columnName,
|
|
417
|
+
targetColumn,
|
|
418
|
+
tenantId
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
transformedData[columnName] = transformedValue;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return transformedData;
|
|
425
|
+
}
|
|
426
|
+
}
|