@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.
Files changed (150) hide show
  1. package/README.md +220 -0
  2. package/dist/index.d.ts +7 -0
  3. package/dist/index.js +34 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/prisma-factory.service.d.ts +9 -0
  6. package/dist/prisma-factory.service.js +47 -0
  7. package/dist/prisma-factory.service.js.map +1 -0
  8. package/dist/prisma.module.d.ts +2 -0
  9. package/dist/prisma.module.js +23 -0
  10. package/dist/prisma.module.js.map +1 -0
  11. package/dist/prisma.service.d.ts +6 -0
  12. package/dist/prisma.service.js +27 -0
  13. package/dist/prisma.service.js.map +1 -0
  14. package/dist/scripts/create-tenant-schemas.d.ts +1 -0
  15. package/dist/scripts/create-tenant-schemas.js +117 -0
  16. package/dist/scripts/create-tenant-schemas.js.map +1 -0
  17. package/dist/scripts/data-migration/batch-migrator.d.ts +25 -0
  18. package/dist/scripts/data-migration/batch-migrator.js +333 -0
  19. package/dist/scripts/data-migration/batch-migrator.js.map +1 -0
  20. package/dist/scripts/data-migration/data-transformer.d.ts +17 -0
  21. package/dist/scripts/data-migration/data-transformer.js +242 -0
  22. package/dist/scripts/data-migration/data-transformer.js.map +1 -0
  23. package/dist/scripts/data-migration/db-connector.d.ts +7 -0
  24. package/dist/scripts/data-migration/db-connector.js +58 -0
  25. package/dist/scripts/data-migration/db-connector.js.map +1 -0
  26. package/dist/scripts/data-migration/dependency-manager.d.ts +9 -0
  27. package/dist/scripts/data-migration/dependency-manager.js +86 -0
  28. package/dist/scripts/data-migration/dependency-manager.js.map +1 -0
  29. package/dist/scripts/data-migration/dependency-resolver.d.ts +18 -0
  30. package/dist/scripts/data-migration/dependency-resolver.js +251 -0
  31. package/dist/scripts/data-migration/dependency-resolver.js.map +1 -0
  32. package/dist/scripts/data-migration/entity-discovery.d.ts +11 -0
  33. package/dist/scripts/data-migration/entity-discovery.js +152 -0
  34. package/dist/scripts/data-migration/entity-discovery.js.map +1 -0
  35. package/dist/scripts/data-migration/foreign-key-manager.d.ts +17 -0
  36. package/dist/scripts/data-migration/foreign-key-manager.js +70 -0
  37. package/dist/scripts/data-migration/foreign-key-manager.js.map +1 -0
  38. package/dist/scripts/data-migration/migration-phases.d.ts +5 -0
  39. package/dist/scripts/data-migration/migration-phases.js +55 -0
  40. package/dist/scripts/data-migration/migration-phases.js.map +1 -0
  41. package/dist/scripts/data-migration/migration-tool.d.ts +29 -0
  42. package/dist/scripts/data-migration/migration-tool.js +250 -0
  43. package/dist/scripts/data-migration/migration-tool.js.map +1 -0
  44. package/dist/scripts/data-migration/phase-generator.d.ts +15 -0
  45. package/dist/scripts/data-migration/phase-generator.js +187 -0
  46. package/dist/scripts/data-migration/phase-generator.js.map +1 -0
  47. package/dist/scripts/data-migration/schema-utils.d.ts +18 -0
  48. package/dist/scripts/data-migration/schema-utils.js +164 -0
  49. package/dist/scripts/data-migration/schema-utils.js.map +1 -0
  50. package/dist/scripts/data-migration/tenant-migrator.d.ts +15 -0
  51. package/dist/scripts/data-migration/tenant-migrator.js +110 -0
  52. package/dist/scripts/data-migration/tenant-migrator.js.map +1 -0
  53. package/dist/scripts/data-migration/typecast-manager.d.ts +5 -0
  54. package/dist/scripts/data-migration/typecast-manager.js +35 -0
  55. package/dist/scripts/data-migration/typecast-manager.js.map +1 -0
  56. package/dist/scripts/data-migration/types.d.ts +34 -0
  57. package/dist/scripts/data-migration/types.js +3 -0
  58. package/dist/scripts/data-migration/types.js.map +1 -0
  59. package/dist/scripts/data-migration.d.ts +22 -0
  60. package/dist/scripts/data-migration.js +593 -0
  61. package/dist/scripts/data-migration.js.map +1 -0
  62. package/dist/scripts/drop-database.d.ts +10 -0
  63. package/dist/scripts/drop-database.js +81 -0
  64. package/dist/scripts/drop-database.js.map +1 -0
  65. package/dist/scripts/error-handler.d.ts +12 -0
  66. package/dist/scripts/error-handler.js +82 -0
  67. package/dist/scripts/error-handler.js.map +1 -0
  68. package/dist/scripts/fix-data-types.d.ts +10 -0
  69. package/dist/scripts/fix-data-types.js +185 -0
  70. package/dist/scripts/fix-data-types.js.map +1 -0
  71. package/dist/scripts/fix-enum-values.d.ts +17 -0
  72. package/dist/scripts/fix-enum-values.js +234 -0
  73. package/dist/scripts/fix-enum-values.js.map +1 -0
  74. package/dist/scripts/fix-schema-discrepancies.d.ts +21 -0
  75. package/dist/scripts/fix-schema-discrepancies.js +240 -0
  76. package/dist/scripts/fix-schema-discrepancies.js.map +1 -0
  77. package/dist/scripts/migrate-schema-structure.d.ts +1 -0
  78. package/dist/scripts/migrate-schema-structure.js +76 -0
  79. package/dist/scripts/migrate-schema-structure.js.map +1 -0
  80. package/dist/scripts/post-migration-validator.d.ts +21 -0
  81. package/dist/scripts/post-migration-validator.js +341 -0
  82. package/dist/scripts/post-migration-validator.js.map +1 -0
  83. package/dist/scripts/pre-migration-validator.d.ts +25 -0
  84. package/dist/scripts/pre-migration-validator.js +491 -0
  85. package/dist/scripts/pre-migration-validator.js.map +1 -0
  86. package/dist/scripts/reset-database.d.ts +17 -0
  87. package/dist/scripts/reset-database.js +202 -0
  88. package/dist/scripts/reset-database.js.map +1 -0
  89. package/dist/scripts/retry-failed-migrations.d.ts +14 -0
  90. package/dist/scripts/retry-failed-migrations.js +301 -0
  91. package/dist/scripts/retry-failed-migrations.js.map +1 -0
  92. package/dist/scripts/run-migration.d.ts +1 -0
  93. package/dist/scripts/run-migration.js +525 -0
  94. package/dist/scripts/run-migration.js.map +1 -0
  95. package/dist/scripts/schema-sync.d.ts +1 -0
  96. package/dist/scripts/schema-sync.js +85 -0
  97. package/dist/scripts/schema-sync.js.map +1 -0
  98. package/dist/scripts/sync-enum-types.d.ts +13 -0
  99. package/dist/scripts/sync-enum-types.js +139 -0
  100. package/dist/scripts/sync-enum-types.js.map +1 -0
  101. package/dist/scripts/sync-enum-values.d.ts +20 -0
  102. package/dist/scripts/sync-enum-values.js +336 -0
  103. package/dist/scripts/sync-enum-values.js.map +1 -0
  104. package/dist/scripts/truncate-database.d.ts +10 -0
  105. package/dist/scripts/truncate-database.js +100 -0
  106. package/dist/scripts/truncate-database.js.map +1 -0
  107. package/dist/scripts/verify-migration-setup.d.ts +11 -0
  108. package/dist/scripts/verify-migration-setup.js +120 -0
  109. package/dist/scripts/verify-migration-setup.js.map +1 -0
  110. package/dist/tsconfig.tsbuildinfo +1 -0
  111. package/migration-config-public.json +95 -0
  112. package/migration-config.json +95 -0
  113. package/package.json +33 -0
  114. package/prisma/migrations/migration_lock.toml +3 -0
  115. package/prisma/schema.prisma +360 -0
  116. package/src/index.ts +23 -0
  117. package/src/prisma-factory.service.ts +41 -0
  118. package/src/prisma.module.ts +10 -0
  119. package/src/prisma.service.ts +17 -0
  120. package/src/scripts/create-tenant-schemas.ts +146 -0
  121. package/src/scripts/data-migration/batch-migrator.ts +569 -0
  122. package/src/scripts/data-migration/data-transformer.ts +377 -0
  123. package/src/scripts/data-migration/db-connector.ts +67 -0
  124. package/src/scripts/data-migration/dependency-resolver.ts +319 -0
  125. package/src/scripts/data-migration/entity-discovery.ts +197 -0
  126. package/src/scripts/data-migration/foreign-key-manager.ts +95 -0
  127. package/src/scripts/data-migration/migration-tool.ts +357 -0
  128. package/src/scripts/data-migration/schema-utils.ts +186 -0
  129. package/src/scripts/data-migration/tenant-migrator.ts +194 -0
  130. package/src/scripts/data-migration/typecast-manager.ts +38 -0
  131. package/src/scripts/data-migration/types.ts +40 -0
  132. package/src/scripts/drop-database.ts +105 -0
  133. package/src/scripts/dump-source-db.sh +62 -0
  134. package/src/scripts/dumps/source_dump_20250413_112626.sql +1527 -0
  135. package/src/scripts/error-handler.ts +118 -0
  136. package/src/scripts/fix-data-types.ts +242 -0
  137. package/src/scripts/fix-enum-values.ts +357 -0
  138. package/src/scripts/fix-schema-discrepancies.ts +318 -0
  139. package/src/scripts/migrate-schema-structure.ts +90 -0
  140. package/src/scripts/post-migration-validator.ts +427 -0
  141. package/src/scripts/pre-migration-validator.ts +611 -0
  142. package/src/scripts/reset-database.ts +264 -0
  143. package/src/scripts/retry-failed-migrations.ts +416 -0
  144. package/src/scripts/run-migration.ts +691 -0
  145. package/src/scripts/schema-sync.ts +129 -0
  146. package/src/scripts/sync-enum-types.ts +171 -0
  147. package/src/scripts/sync-enum-values.ts +563 -0
  148. package/src/scripts/truncate-database.ts +124 -0
  149. package/src/scripts/verify-migration-setup.ts +136 -0
  150. 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
+ }