@javalabs/prisma-client 1.0.0
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/README.md +220 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/prisma-factory.service.d.ts +9 -0
- package/dist/prisma-factory.service.js +47 -0
- package/dist/prisma-factory.service.js.map +1 -0
- package/dist/prisma.module.d.ts +2 -0
- package/dist/prisma.module.js +23 -0
- package/dist/prisma.module.js.map +1 -0
- package/dist/prisma.service.d.ts +6 -0
- package/dist/prisma.service.js +27 -0
- package/dist/prisma.service.js.map +1 -0
- package/dist/scripts/create-tenant-schemas.d.ts +1 -0
- package/dist/scripts/create-tenant-schemas.js +117 -0
- package/dist/scripts/create-tenant-schemas.js.map +1 -0
- package/dist/scripts/data-migration/batch-migrator.d.ts +25 -0
- package/dist/scripts/data-migration/batch-migrator.js +333 -0
- package/dist/scripts/data-migration/batch-migrator.js.map +1 -0
- package/dist/scripts/data-migration/data-transformer.d.ts +17 -0
- package/dist/scripts/data-migration/data-transformer.js +242 -0
- package/dist/scripts/data-migration/data-transformer.js.map +1 -0
- package/dist/scripts/data-migration/db-connector.d.ts +7 -0
- package/dist/scripts/data-migration/db-connector.js +58 -0
- package/dist/scripts/data-migration/db-connector.js.map +1 -0
- package/dist/scripts/data-migration/dependency-manager.d.ts +9 -0
- package/dist/scripts/data-migration/dependency-manager.js +86 -0
- package/dist/scripts/data-migration/dependency-manager.js.map +1 -0
- package/dist/scripts/data-migration/dependency-resolver.d.ts +18 -0
- package/dist/scripts/data-migration/dependency-resolver.js +251 -0
- package/dist/scripts/data-migration/dependency-resolver.js.map +1 -0
- package/dist/scripts/data-migration/entity-discovery.d.ts +11 -0
- package/dist/scripts/data-migration/entity-discovery.js +152 -0
- package/dist/scripts/data-migration/entity-discovery.js.map +1 -0
- package/dist/scripts/data-migration/foreign-key-manager.d.ts +17 -0
- package/dist/scripts/data-migration/foreign-key-manager.js +70 -0
- package/dist/scripts/data-migration/foreign-key-manager.js.map +1 -0
- package/dist/scripts/data-migration/migration-phases.d.ts +5 -0
- package/dist/scripts/data-migration/migration-phases.js +55 -0
- package/dist/scripts/data-migration/migration-phases.js.map +1 -0
- package/dist/scripts/data-migration/migration-tool.d.ts +29 -0
- package/dist/scripts/data-migration/migration-tool.js +250 -0
- package/dist/scripts/data-migration/migration-tool.js.map +1 -0
- package/dist/scripts/data-migration/phase-generator.d.ts +15 -0
- package/dist/scripts/data-migration/phase-generator.js +187 -0
- package/dist/scripts/data-migration/phase-generator.js.map +1 -0
- package/dist/scripts/data-migration/schema-utils.d.ts +18 -0
- package/dist/scripts/data-migration/schema-utils.js +164 -0
- package/dist/scripts/data-migration/schema-utils.js.map +1 -0
- package/dist/scripts/data-migration/tenant-migrator.d.ts +15 -0
- package/dist/scripts/data-migration/tenant-migrator.js +110 -0
- package/dist/scripts/data-migration/tenant-migrator.js.map +1 -0
- package/dist/scripts/data-migration/typecast-manager.d.ts +5 -0
- package/dist/scripts/data-migration/typecast-manager.js +35 -0
- package/dist/scripts/data-migration/typecast-manager.js.map +1 -0
- package/dist/scripts/data-migration/types.d.ts +34 -0
- package/dist/scripts/data-migration/types.js +3 -0
- package/dist/scripts/data-migration/types.js.map +1 -0
- package/dist/scripts/data-migration.d.ts +22 -0
- package/dist/scripts/data-migration.js +593 -0
- package/dist/scripts/data-migration.js.map +1 -0
- package/dist/scripts/drop-database.d.ts +10 -0
- package/dist/scripts/drop-database.js +81 -0
- package/dist/scripts/drop-database.js.map +1 -0
- package/dist/scripts/error-handler.d.ts +12 -0
- package/dist/scripts/error-handler.js +82 -0
- package/dist/scripts/error-handler.js.map +1 -0
- package/dist/scripts/fix-data-types.d.ts +10 -0
- package/dist/scripts/fix-data-types.js +185 -0
- package/dist/scripts/fix-data-types.js.map +1 -0
- package/dist/scripts/fix-enum-values.d.ts +17 -0
- package/dist/scripts/fix-enum-values.js +234 -0
- package/dist/scripts/fix-enum-values.js.map +1 -0
- package/dist/scripts/fix-schema-discrepancies.d.ts +21 -0
- package/dist/scripts/fix-schema-discrepancies.js +240 -0
- package/dist/scripts/fix-schema-discrepancies.js.map +1 -0
- package/dist/scripts/migrate-schema-structure.d.ts +1 -0
- package/dist/scripts/migrate-schema-structure.js +76 -0
- package/dist/scripts/migrate-schema-structure.js.map +1 -0
- package/dist/scripts/post-migration-validator.d.ts +21 -0
- package/dist/scripts/post-migration-validator.js +341 -0
- package/dist/scripts/post-migration-validator.js.map +1 -0
- package/dist/scripts/pre-migration-validator.d.ts +25 -0
- package/dist/scripts/pre-migration-validator.js +491 -0
- package/dist/scripts/pre-migration-validator.js.map +1 -0
- package/dist/scripts/reset-database.d.ts +17 -0
- package/dist/scripts/reset-database.js +202 -0
- package/dist/scripts/reset-database.js.map +1 -0
- package/dist/scripts/retry-failed-migrations.d.ts +14 -0
- package/dist/scripts/retry-failed-migrations.js +301 -0
- package/dist/scripts/retry-failed-migrations.js.map +1 -0
- package/dist/scripts/run-migration.d.ts +1 -0
- package/dist/scripts/run-migration.js +525 -0
- package/dist/scripts/run-migration.js.map +1 -0
- package/dist/scripts/schema-sync.d.ts +1 -0
- package/dist/scripts/schema-sync.js +85 -0
- package/dist/scripts/schema-sync.js.map +1 -0
- package/dist/scripts/sync-enum-types.d.ts +13 -0
- package/dist/scripts/sync-enum-types.js +139 -0
- package/dist/scripts/sync-enum-types.js.map +1 -0
- package/dist/scripts/sync-enum-values.d.ts +20 -0
- package/dist/scripts/sync-enum-values.js +336 -0
- package/dist/scripts/sync-enum-values.js.map +1 -0
- package/dist/scripts/truncate-database.d.ts +10 -0
- package/dist/scripts/truncate-database.js +100 -0
- package/dist/scripts/truncate-database.js.map +1 -0
- package/dist/scripts/verify-migration-setup.d.ts +11 -0
- package/dist/scripts/verify-migration-setup.js +120 -0
- package/dist/scripts/verify-migration-setup.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/migration-config-public.json +95 -0
- package/migration-config.json +95 -0
- package/package.json +33 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +360 -0
- package/src/index.ts +23 -0
- package/src/prisma-factory.service.ts +41 -0
- package/src/prisma.module.ts +10 -0
- package/src/prisma.service.ts +17 -0
- package/src/scripts/create-tenant-schemas.ts +146 -0
- package/src/scripts/data-migration/batch-migrator.ts +569 -0
- package/src/scripts/data-migration/data-transformer.ts +377 -0
- package/src/scripts/data-migration/db-connector.ts +67 -0
- package/src/scripts/data-migration/dependency-resolver.ts +319 -0
- package/src/scripts/data-migration/entity-discovery.ts +197 -0
- package/src/scripts/data-migration/foreign-key-manager.ts +95 -0
- package/src/scripts/data-migration/migration-tool.ts +357 -0
- package/src/scripts/data-migration/schema-utils.ts +186 -0
- package/src/scripts/data-migration/tenant-migrator.ts +194 -0
- package/src/scripts/data-migration/typecast-manager.ts +38 -0
- package/src/scripts/data-migration/types.ts +40 -0
- package/src/scripts/drop-database.ts +105 -0
- package/src/scripts/dump-source-db.sh +62 -0
- package/src/scripts/dumps/source_dump_20250413_112626.sql +1527 -0
- package/src/scripts/error-handler.ts +118 -0
- package/src/scripts/fix-data-types.ts +242 -0
- package/src/scripts/fix-enum-values.ts +357 -0
- package/src/scripts/fix-schema-discrepancies.ts +318 -0
- package/src/scripts/migrate-schema-structure.ts +90 -0
- package/src/scripts/post-migration-validator.ts +427 -0
- package/src/scripts/pre-migration-validator.ts +611 -0
- package/src/scripts/reset-database.ts +264 -0
- package/src/scripts/retry-failed-migrations.ts +416 -0
- package/src/scripts/run-migration.ts +691 -0
- package/src/scripts/schema-sync.ts +129 -0
- package/src/scripts/sync-enum-types.ts +171 -0
- package/src/scripts/sync-enum-values.ts +563 -0
- package/src/scripts/truncate-database.ts +124 -0
- package/src/scripts/verify-migration-setup.ts +136 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
import { ColumnSchema, EnumCastValue } from "./types";
|
|
3
|
+
import { SchemaUtils } from "./schema-utils";
|
|
4
|
+
|
|
5
|
+
export class DataTransformer {
|
|
6
|
+
private readonly logger = new Logger("DataTransformer");
|
|
7
|
+
private enumValuesCache: Map<string, any> = new Map();
|
|
8
|
+
private columnValuesCache: Map<string, Map<string, any>> = new Map();
|
|
9
|
+
|
|
10
|
+
constructor(public readonly schemaUtils: SchemaUtils) {}
|
|
11
|
+
|
|
12
|
+
async validateEnumValue(
|
|
13
|
+
schemaName: string,
|
|
14
|
+
enumType: string,
|
|
15
|
+
value: string
|
|
16
|
+
): Promise<string | null> {
|
|
17
|
+
try {
|
|
18
|
+
const cacheKey = `${schemaName}.${enumType}`;
|
|
19
|
+
if (!this.enumValuesCache.has(cacheKey)) {
|
|
20
|
+
const query = `
|
|
21
|
+
SELECT e.enumlabel
|
|
22
|
+
FROM pg_type t
|
|
23
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
24
|
+
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
25
|
+
WHERE t.typname = $1 AND n.nspname = $2
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const result = await this.schemaUtils.queryTargetDb(query, [
|
|
29
|
+
enumType,
|
|
30
|
+
schemaName,
|
|
31
|
+
]);
|
|
32
|
+
const uniqueValues: Set<string> = new Set(
|
|
33
|
+
result.rows.map((row): string => String(row.enumlabel))
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// También almacenar un mapa de valores normalizados a valores originales
|
|
37
|
+
const normalizedMap = new Map<string, string>();
|
|
38
|
+
result.rows.forEach((row) => {
|
|
39
|
+
const original = String(row.enumlabel);
|
|
40
|
+
const normalized = original.toLowerCase().trim();
|
|
41
|
+
normalizedMap.set(normalized, original);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.enumValuesCache.set(cacheKey, uniqueValues);
|
|
45
|
+
this.enumValuesCache.set(`${cacheKey}_normalized`, normalizedMap);
|
|
46
|
+
|
|
47
|
+
this.logger.log(
|
|
48
|
+
`Valid values for ${enumType}: ${[...uniqueValues].join(", ")}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const validValues = this.enumValuesCache.get(cacheKey);
|
|
53
|
+
const normalizedMap = this.enumValuesCache.get(
|
|
54
|
+
`${cacheKey}_normalized`
|
|
55
|
+
) as unknown as Map<string, string>;
|
|
56
|
+
|
|
57
|
+
if (!validValues || !normalizedMap) {
|
|
58
|
+
throw new Error(`No valid values found for enum ${enumType}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Verificar coincidencia exacta primero
|
|
62
|
+
if (validValues.has(value)) {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Luego intentar con valor normalizado
|
|
67
|
+
const normalizedValue = value.toLowerCase().trim();
|
|
68
|
+
if (normalizedMap.has(normalizedValue)) {
|
|
69
|
+
// Devolver el valor original correcto, no el normalizado
|
|
70
|
+
return normalizedMap.get(normalizedValue);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.logger.warn(
|
|
74
|
+
`Invalid enum value "${value}" for type ${enumType}. Valid values are: ${[
|
|
75
|
+
...validValues,
|
|
76
|
+
].join(", ")}`
|
|
77
|
+
);
|
|
78
|
+
return null;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this.logger.error(
|
|
81
|
+
`Error validating enum value "${value}" for type ${enumType}: ${error.message}`
|
|
82
|
+
);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
transformToNumeric(value: any): number | null {
|
|
88
|
+
if (value === null || value === "") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const numericValue = parseFloat(value);
|
|
93
|
+
if (isNaN(numericValue)) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return numericValue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async getDefaultEnumValue(
|
|
101
|
+
enumType: string,
|
|
102
|
+
tenantId: string = "public"
|
|
103
|
+
): Promise<string | null> {
|
|
104
|
+
try {
|
|
105
|
+
// Obtener todos los valores posibles del enum desde la base de datos
|
|
106
|
+
const query = `
|
|
107
|
+
SELECT e.enumlabel
|
|
108
|
+
FROM pg_type t
|
|
109
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
110
|
+
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
111
|
+
WHERE t.typname = $1 AND n.nspname = $2
|
|
112
|
+
ORDER BY e.enumsortorder
|
|
113
|
+
LIMIT 1
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const result = await this.schemaUtils.queryTargetDb(query, [
|
|
117
|
+
enumType,
|
|
118
|
+
tenantId,
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
// Retornar el primer valor del enum como valor por defecto
|
|
122
|
+
if (result.rows.length > 0) {
|
|
123
|
+
return result.rows[0].enumlabel;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
this.logger.error(
|
|
129
|
+
`Error getting default enum value for ${enumType}: ${error.message}`
|
|
130
|
+
);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async prepareEnumValue(
|
|
136
|
+
tenantId: string,
|
|
137
|
+
enumType: string,
|
|
138
|
+
value: string
|
|
139
|
+
): Promise<EnumCastValue | null> {
|
|
140
|
+
if (value === null || value === undefined || value === "") {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const validatedValue = await this.validateEnumValue(
|
|
145
|
+
tenantId,
|
|
146
|
+
enumType,
|
|
147
|
+
value
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (validatedValue === null) {
|
|
151
|
+
// Obtener valor por defecto dinámicamente
|
|
152
|
+
const defaultValue = await this.getDefaultEnumValue(enumType, tenantId);
|
|
153
|
+
if (defaultValue) {
|
|
154
|
+
this.logger.warn(
|
|
155
|
+
`Using first enum value '${defaultValue}' as default for invalid value '${value}' of type ${enumType}`
|
|
156
|
+
);
|
|
157
|
+
return {
|
|
158
|
+
needsEnumCast: true,
|
|
159
|
+
value: defaultValue,
|
|
160
|
+
enumType,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
needsEnumCast: true,
|
|
168
|
+
value: validatedValue,
|
|
169
|
+
enumType,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async transformColumnValue(
|
|
174
|
+
value: any,
|
|
175
|
+
columnName: string,
|
|
176
|
+
targetColumn: ColumnSchema,
|
|
177
|
+
tenantId: string
|
|
178
|
+
): Promise<any> {
|
|
179
|
+
// Log input values for debugging
|
|
180
|
+
this.logger.debug(
|
|
181
|
+
`Transforming column ${columnName}, value: ${JSON.stringify(
|
|
182
|
+
value
|
|
183
|
+
)}, type: ${targetColumn.udt_name}`
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (value === null && targetColumn.is_nullable === "YES") {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// Handle enum types
|
|
192
|
+
if (targetColumn.udt_name.startsWith("enum_")) {
|
|
193
|
+
// Handle JSONB/object to enum conversion
|
|
194
|
+
if (typeof value === "object" && value !== null) {
|
|
195
|
+
const stringValue = this.extractEnumValueFromObject(value);
|
|
196
|
+
if (stringValue) {
|
|
197
|
+
return await this.prepareEnumValue(
|
|
198
|
+
tenantId,
|
|
199
|
+
targetColumn.udt_name,
|
|
200
|
+
stringValue
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Handle direct string values
|
|
206
|
+
if (typeof value === "string") {
|
|
207
|
+
return await this.prepareEnumValue(
|
|
208
|
+
tenantId,
|
|
209
|
+
targetColumn.udt_name,
|
|
210
|
+
value
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// If no valid value found, get default
|
|
215
|
+
const defaultValue = await this.getDefaultEnumValue(
|
|
216
|
+
targetColumn.udt_name,
|
|
217
|
+
tenantId
|
|
218
|
+
);
|
|
219
|
+
if (defaultValue) {
|
|
220
|
+
this.logger.warn(
|
|
221
|
+
`Using default enum value '${defaultValue}' for column ${columnName}`
|
|
222
|
+
);
|
|
223
|
+
return {
|
|
224
|
+
needsEnumCast: true,
|
|
225
|
+
value: defaultValue,
|
|
226
|
+
enumType: targetColumn.udt_name,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Handle numeric types
|
|
232
|
+
if (
|
|
233
|
+
targetColumn.udt_name === "numeric" ||
|
|
234
|
+
targetColumn.data_type === "decimal"
|
|
235
|
+
) {
|
|
236
|
+
const numericValue = this.transformToNumeric(value);
|
|
237
|
+
if (numericValue !== null || targetColumn.is_nullable === "YES") {
|
|
238
|
+
return numericValue;
|
|
239
|
+
}
|
|
240
|
+
return 0; // Default value for non-nullable numeric fields
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Handle boolean types
|
|
244
|
+
if (
|
|
245
|
+
targetColumn.udt_name === "bool" ||
|
|
246
|
+
targetColumn.data_type === "boolean"
|
|
247
|
+
) {
|
|
248
|
+
if (typeof value === "string") {
|
|
249
|
+
return value.toLowerCase() === "true" || value === "1";
|
|
250
|
+
}
|
|
251
|
+
return Boolean(value);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Handle date/timestamp types
|
|
255
|
+
if (
|
|
256
|
+
targetColumn.data_type.includes("timestamp") ||
|
|
257
|
+
targetColumn.data_type.includes("date")
|
|
258
|
+
) {
|
|
259
|
+
if (!value) return null;
|
|
260
|
+
const date = new Date(value);
|
|
261
|
+
return isNaN(date.getTime()) ? null : date.toISOString();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Handle JSON/JSONB types
|
|
265
|
+
if (
|
|
266
|
+
targetColumn.data_type === "json" ||
|
|
267
|
+
targetColumn.data_type === "jsonb"
|
|
268
|
+
) {
|
|
269
|
+
if (typeof value === "string") {
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(value);
|
|
272
|
+
} catch {
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return value;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Default case: return value as-is
|
|
280
|
+
return value;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
this.logger.error(
|
|
283
|
+
`Error transforming value for column ${columnName}: ${error.message}`
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// Return appropriate default values based on column type
|
|
287
|
+
if (targetColumn.is_nullable === "YES") {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return this.getDefaultValueForType(targetColumn.udt_name);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private extractEnumValueFromObject(value: any): string | null {
|
|
296
|
+
const possibleKeys = ["type", "value", "document_type", "status", "state"];
|
|
297
|
+
|
|
298
|
+
for (const key of possibleKeys) {
|
|
299
|
+
if (value[key] && typeof value[key] === "string") {
|
|
300
|
+
return value[key];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Try to find any string value in the object
|
|
305
|
+
for (const val of Object.values(value)) {
|
|
306
|
+
if (typeof val === "string") {
|
|
307
|
+
return val;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private getDefaultValueForType(udtName: string): any {
|
|
315
|
+
switch (udtName) {
|
|
316
|
+
case "int4":
|
|
317
|
+
case "int8":
|
|
318
|
+
case "numeric":
|
|
319
|
+
return 0;
|
|
320
|
+
case "bool":
|
|
321
|
+
return false;
|
|
322
|
+
case "text":
|
|
323
|
+
case "varchar":
|
|
324
|
+
return "";
|
|
325
|
+
case "jsonb":
|
|
326
|
+
case "json":
|
|
327
|
+
return {};
|
|
328
|
+
default:
|
|
329
|
+
if (udtName.startsWith("enum_")) {
|
|
330
|
+
return null; // Enums should be handled by prepareEnumValue
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async transformRecord(
|
|
337
|
+
record: any,
|
|
338
|
+
sourceSchema: ColumnSchema[],
|
|
339
|
+
targetSchema: ColumnSchema[],
|
|
340
|
+
tenantId: string
|
|
341
|
+
): Promise<any> {
|
|
342
|
+
const transformedData: any = {};
|
|
343
|
+
|
|
344
|
+
for (const [key, value] of Object.entries(record)) {
|
|
345
|
+
// Primero intentar encontrar una coincidencia exacta
|
|
346
|
+
let targetColumn = targetSchema.find(
|
|
347
|
+
(col) => col.column_name === key
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Si no se encuentra coincidencia exacta, intentar case-insensitive
|
|
351
|
+
if (!targetColumn) {
|
|
352
|
+
targetColumn = targetSchema.find(
|
|
353
|
+
(col) => col.column_name.toLowerCase() === key.toLowerCase()
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!targetColumn) {
|
|
358
|
+
this.logger.warn(`Column ${key} not found in target schema, skipping`);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Usar el nombre exacto de la columna del schema destino
|
|
363
|
+
const columnName = targetColumn.column_name;
|
|
364
|
+
|
|
365
|
+
const transformedValue = await this.transformColumnValue(
|
|
366
|
+
value,
|
|
367
|
+
columnName,
|
|
368
|
+
targetColumn,
|
|
369
|
+
tenantId
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
transformedData[columnName] = transformedValue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return transformedData;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
import * as pg from "pg";
|
|
3
|
+
import * as dotenv from "dotenv";
|
|
4
|
+
import { DatabaseConnections } from "./types";
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
export class DatabaseConnector {
|
|
9
|
+
static createConnections(): DatabaseConnections {
|
|
10
|
+
// Source database connection (the one we're migrating from)
|
|
11
|
+
const sourcePool = new pg.Pool({
|
|
12
|
+
connectionString: process.env.SOURCE_DATABASE_URL,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Target database connection (our multi-tenant database)
|
|
16
|
+
const targetPool = new pg.Pool({
|
|
17
|
+
connectionString: process.env.DATABASE_URL,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Source Prisma client
|
|
21
|
+
const sourcePrisma = new PrismaClient({
|
|
22
|
+
datasources: {
|
|
23
|
+
db: {
|
|
24
|
+
url: process.env.SOURCE_DATABASE_URL,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Target Prisma client
|
|
30
|
+
const targetPrisma = new PrismaClient({
|
|
31
|
+
datasources: {
|
|
32
|
+
db: {
|
|
33
|
+
url: process.env.DATABASE_URL,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
sourcePrisma,
|
|
40
|
+
targetPrisma,
|
|
41
|
+
sourcePool,
|
|
42
|
+
targetPool,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static createTenantPrismaClient(tenantId: string): PrismaClient {
|
|
47
|
+
return new PrismaClient({
|
|
48
|
+
datasources: {
|
|
49
|
+
db: {
|
|
50
|
+
url: `${process.env.DATABASE_URL}?schema=${tenantId}`,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
// Increase transaction timeout to 2 minutes
|
|
54
|
+
transactionOptions: {
|
|
55
|
+
maxWait: 120000, // 2 minutes max wait time
|
|
56
|
+
timeout: 120000, // 2 minutes timeout
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static async cleanup(connections: DatabaseConnections): Promise<void> {
|
|
62
|
+
await connections.sourcePrisma.$disconnect();
|
|
63
|
+
await connections.targetPrisma.$disconnect();
|
|
64
|
+
await connections.sourcePool.end();
|
|
65
|
+
await connections.targetPool.end();
|
|
66
|
+
}
|
|
67
|
+
}
|