@javalabs/prisma-client 1.0.27 → 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.
Files changed (50) hide show
  1. package/.github/CODEOWNERS +1 -1
  2. package/README.md +269 -269
  3. package/migration-config.json +63 -63
  4. package/migration-config.json.bk +95 -95
  5. package/migrations/add_reserved_amount.sql +7 -7
  6. package/package.json +44 -44
  7. package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
  8. package/prisma/schema.prisma +609 -601
  9. package/src/index.ts +23 -23
  10. package/src/prisma-factory.service.ts +40 -40
  11. package/src/prisma.module.ts +9 -9
  12. package/src/prisma.service.ts +16 -16
  13. package/src/scripts/add-uuid-to-table.ts +138 -138
  14. package/src/scripts/create-tenant-schemas.ts +145 -145
  15. package/src/scripts/data-migration/batch-migrator.ts +248 -248
  16. package/src/scripts/data-migration/data-transformer.ts +426 -426
  17. package/src/scripts/data-migration/db-connector.ts +120 -120
  18. package/src/scripts/data-migration/dependency-resolver.ts +174 -174
  19. package/src/scripts/data-migration/entity-discovery.ts +196 -196
  20. package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
  21. package/src/scripts/data-migration/migration-config.json +63 -63
  22. package/src/scripts/data-migration/migration-tool.ts +509 -509
  23. package/src/scripts/data-migration/schema-utils.ts +248 -248
  24. package/src/scripts/data-migration/tenant-migrator.ts +201 -201
  25. package/src/scripts/data-migration/typecast-manager.ts +193 -193
  26. package/src/scripts/data-migration/types.ts +113 -113
  27. package/src/scripts/database-initializer.ts +49 -49
  28. package/src/scripts/drop-database.ts +104 -104
  29. package/src/scripts/dump-source-db.sh +61 -61
  30. package/src/scripts/encrypt-user-passwords.ts +36 -36
  31. package/src/scripts/error-handler.ts +117 -117
  32. package/src/scripts/fix-data-types.ts +241 -241
  33. package/src/scripts/fix-enum-values.ts +357 -357
  34. package/src/scripts/fix-schema-discrepancies.ts +317 -317
  35. package/src/scripts/fix-table-indexes.ts +601 -601
  36. package/src/scripts/migrate-schema-structure.ts +90 -90
  37. package/src/scripts/migrate-uuid.ts +76 -76
  38. package/src/scripts/post-migration-validator.ts +526 -526
  39. package/src/scripts/pre-migration-validator.ts +610 -610
  40. package/src/scripts/reset-database.ts +263 -263
  41. package/src/scripts/retry-failed-migrations.ts +416 -416
  42. package/src/scripts/run-migration.ts +707 -707
  43. package/src/scripts/schema-sync.ts +128 -128
  44. package/src/scripts/sequence-sync-cli.ts +416 -416
  45. package/src/scripts/sequence-synchronizer.ts +127 -127
  46. package/src/scripts/sync-enum-types.ts +170 -170
  47. package/src/scripts/sync-enum-values.ts +563 -563
  48. package/src/scripts/truncate-database.ts +123 -123
  49. package/src/scripts/verify-migration-setup.ts +135 -135
  50. package/tsconfig.json +17 -17
@@ -1,242 +1,242 @@
1
- import { PrismaClient } from "@prisma/client";
2
- import * as dotenv from "dotenv";
3
- import { Logger } from "@nestjs/common";
4
- import * as pg from "pg";
5
-
6
- dotenv.config();
7
-
8
- export class DataTypeFixer {
9
- private readonly logger = new Logger("DataTypeFixer");
10
- private readonly sourcePool: pg.Pool;
11
- private readonly targetPool: pg.Pool;
12
-
13
- constructor() {
14
- // Source database connection
15
- this.sourcePool = new pg.Pool({
16
- connectionString: process.env.SOURCE_DATABASE_URL,
17
- });
18
-
19
- // Target database connection
20
- this.targetPool = new pg.Pool({
21
- connectionString: process.env.DATABASE_URL,
22
- });
23
- }
24
-
25
- async fixDataTypes() {
26
- try {
27
- this.logger.log("Starting data type fixing process");
28
-
29
- // Get all schemas (tenants) from the target database
30
- const schemasQuery = `
31
- SELECT schema_name
32
- FROM information_schema.schemata
33
- WHERE schema_name NOT IN ('public', 'information_schema', 'pg_catalog', 'pg_toast')
34
- AND schema_name NOT LIKE 'pg_%';
35
- `;
36
-
37
- const schemasResult = await this.targetPool.query(schemasQuery);
38
- const schemas = schemasResult.rows.map(row => row.schema_name);
39
-
40
- this.logger.log(`Found ${schemas.length} schemas to process`);
41
-
42
- // Process each schema
43
- for (const schema of schemas) {
44
- await this.fixNumericFieldsForSchema(schema);
45
- await this.fixEnumFieldsForSchema(schema);
46
- }
47
-
48
- this.logger.log("Data type fixing completed successfully");
49
- } catch (error) {
50
- this.logger.error(
51
- `Error during data type fixing: ${error.message}`,
52
- error.stack
53
- );
54
- } finally {
55
- await this.cleanup();
56
- }
57
- }
58
-
59
- private async fixNumericFieldsForSchema(schema: string) {
60
- this.logger.log(`Processing numeric fields for schema: ${schema}`);
61
-
62
- try {
63
- // Get all tables with numeric columns in this schema
64
- const numericColumnsQuery = `
65
- SELECT
66
- table_name,
67
- column_name
68
- FROM
69
- information_schema.columns
70
- WHERE
71
- table_schema = $1
72
- AND data_type = 'numeric'
73
- `;
74
-
75
- const numericColumnsResult = await this.targetPool.query(numericColumnsQuery, [schema]);
76
-
77
- // Process each table with numeric columns
78
- for (const row of numericColumnsResult.rows) {
79
- const tableName = row.table_name;
80
- const columnName = row.column_name;
81
-
82
- this.logger.log(`Fixing numeric column ${columnName} in table ${schema}.${tableName}`);
83
-
84
- // Update the column to cast text values to numeric
85
- const updateQuery = `
86
- UPDATE "${schema}"."${tableName}"
87
- SET "${columnName}" = CASE
88
- WHEN "${columnName}" IS NULL THEN NULL
89
- WHEN "${columnName}" = '' THEN NULL
90
- ELSE CAST("${columnName}" AS NUMERIC)
91
- END
92
- WHERE "${columnName}" IS NOT NULL
93
- AND "${columnName}" != ''
94
- AND "${columnName}" ~ '^[0-9]+(\.[0-9]+)?$';
95
- `;
96
-
97
- try {
98
- await this.targetPool.query(updateQuery);
99
- this.logger.log(`Successfully fixed numeric column ${columnName} in table ${schema}.${tableName}`);
100
- } catch (updateError) {
101
- this.logger.error(
102
- `Error fixing numeric column ${columnName} in table ${schema}.${tableName}: ${updateError.message}`
103
- );
104
- }
105
- }
106
-
107
- this.logger.log(`Completed processing numeric fields for schema: ${schema}`);
108
- } catch (error) {
109
- this.logger.error(
110
- `Error processing numeric fields for schema ${schema}: ${error.message}`,
111
- error.stack
112
- );
113
- }
114
- }
115
-
116
- private async fixEnumFieldsForSchema(schema: string) {
117
- this.logger.log(`Processing enum fields for schema: ${schema}`);
118
-
119
- try {
120
- // Get all tables with enum columns in this schema
121
- const enumColumnsQuery = `
122
- SELECT
123
- c.table_name,
124
- c.column_name,
125
- c.udt_name
126
- FROM
127
- information_schema.columns c
128
- WHERE
129
- c.table_schema = $1
130
- AND c.data_type = 'USER-DEFINED'
131
- AND c.udt_name LIKE 'enum_%'
132
- `;
133
-
134
- const enumColumnsResult = await this.targetPool.query(enumColumnsQuery, [schema]);
135
-
136
- // Process each table with enum columns
137
- for (const row of enumColumnsResult.rows) {
138
- const tableName = row.table_name;
139
- const columnName = row.column_name;
140
- const enumTypeName = row.udt_name;
141
-
142
- this.logger.log(`Fixing enum column ${columnName} (${enumTypeName}) in table ${schema}.${tableName}`);
143
-
144
- // Get the valid enum values
145
- const enumValuesQuery = `
146
- SELECT e.enumlabel
147
- FROM pg_enum e
148
- JOIN pg_type t ON e.enumtypid = t.oid
149
- WHERE t.typname = $1
150
- ORDER BY e.enumsortorder
151
- `;
152
-
153
- const enumValuesResult = await this.targetPool.query(enumValuesQuery, [enumTypeName]);
154
- const validEnumValues = enumValuesResult.rows.map(r => r.enumlabel);
155
-
156
- if (validEnumValues.length === 0) {
157
- this.logger.warn(`No enum values found for type ${enumTypeName}. Skipping.`);
158
- continue;
159
- }
160
-
161
- this.logger.log(`Valid values for ${enumTypeName}: ${validEnumValues.join(', ')}`);
162
-
163
- // Get current values in the column
164
- const currentValuesQuery = `
165
- SELECT DISTINCT "${columnName}"
166
- FROM "${schema}"."${tableName}"
167
- WHERE "${columnName}" IS NOT NULL
168
- `;
169
-
170
- const currentValuesResult = await this.targetPool.query(currentValuesQuery);
171
- const currentValues = currentValuesResult.rows.map(r => r[columnName]);
172
-
173
- // Find invalid values
174
- const invalidValues = currentValues.filter(val => !validEnumValues.includes(val));
175
-
176
- if (invalidValues.length > 0) {
177
- this.logger.log(`Found invalid values for enum ${enumTypeName}: ${invalidValues.join(', ')}`);
178
-
179
- // For each invalid value, try to map it to a valid one or set to NULL
180
- for (const invalidValue of invalidValues) {
181
- // Try to find a case-insensitive match
182
- const matchingValidValue = validEnumValues.find(
183
- v => v.toLowerCase() === invalidValue.toLowerCase()
184
- );
185
-
186
- let updateQuery;
187
- if (matchingValidValue) {
188
- // If we found a case-insensitive match, use it
189
- updateQuery = `
190
- UPDATE "${schema}"."${tableName}"
191
- SET "${columnName}" = $1
192
- WHERE "${columnName}" = $2
193
- `;
194
- await this.targetPool.query(updateQuery, [matchingValidValue, invalidValue]);
195
- this.logger.log(`Updated invalid value "${invalidValue}" to "${matchingValidValue}"`);
196
- } else {
197
- // Otherwise set to NULL or default value
198
- updateQuery = `
199
- UPDATE "${schema}"."${tableName}"
200
- SET "${columnName}" = NULL
201
- WHERE "${columnName}" = $1
202
- `;
203
- await this.targetPool.query(updateQuery, [invalidValue]);
204
- this.logger.log(`Set invalid value "${invalidValue}" to NULL`);
205
- }
206
- }
207
- } else {
208
- this.logger.log(`No invalid enum values found for ${columnName} in ${schema}.${tableName}`);
209
- }
210
- }
211
-
212
- this.logger.log(`Completed processing enum fields for schema: ${schema}`);
213
- } catch (error) {
214
- this.logger.error(
215
- `Error processing enum fields for schema ${schema}: ${error.message}`,
216
- error.stack
217
- );
218
- }
219
- }
220
-
221
- private async cleanup() {
222
- this.logger.log("Cleaning up database connections");
223
- await this.sourcePool.end();
224
- await this.targetPool.end();
225
- }
226
- }
227
-
228
- // Script para ejecutar desde línea de comandos
229
- if (require.main === module) {
230
- const run = async () => {
231
- try {
232
- const fixer = new DataTypeFixer();
233
- await fixer.fixDataTypes();
234
- process.exit(0);
235
- } catch (error) {
236
- console.error("Error:", error.message);
237
- process.exit(1);
238
- }
239
- };
240
-
241
- run();
1
+ import { PrismaClient } from "@prisma/client";
2
+ import * as dotenv from "dotenv";
3
+ import { Logger } from "@nestjs/common";
4
+ import * as pg from "pg";
5
+
6
+ dotenv.config();
7
+
8
+ export class DataTypeFixer {
9
+ private readonly logger = new Logger("DataTypeFixer");
10
+ private readonly sourcePool: pg.Pool;
11
+ private readonly targetPool: pg.Pool;
12
+
13
+ constructor() {
14
+ // Source database connection
15
+ this.sourcePool = new pg.Pool({
16
+ connectionString: process.env.SOURCE_DATABASE_URL,
17
+ });
18
+
19
+ // Target database connection
20
+ this.targetPool = new pg.Pool({
21
+ connectionString: process.env.DATABASE_URL,
22
+ });
23
+ }
24
+
25
+ async fixDataTypes() {
26
+ try {
27
+ this.logger.log("Starting data type fixing process");
28
+
29
+ // Get all schemas (tenants) from the target database
30
+ const schemasQuery = `
31
+ SELECT schema_name
32
+ FROM information_schema.schemata
33
+ WHERE schema_name NOT IN ('public', 'information_schema', 'pg_catalog', 'pg_toast')
34
+ AND schema_name NOT LIKE 'pg_%';
35
+ `;
36
+
37
+ const schemasResult = await this.targetPool.query(schemasQuery);
38
+ const schemas = schemasResult.rows.map(row => row.schema_name);
39
+
40
+ this.logger.log(`Found ${schemas.length} schemas to process`);
41
+
42
+ // Process each schema
43
+ for (const schema of schemas) {
44
+ await this.fixNumericFieldsForSchema(schema);
45
+ await this.fixEnumFieldsForSchema(schema);
46
+ }
47
+
48
+ this.logger.log("Data type fixing completed successfully");
49
+ } catch (error) {
50
+ this.logger.error(
51
+ `Error during data type fixing: ${error.message}`,
52
+ error.stack
53
+ );
54
+ } finally {
55
+ await this.cleanup();
56
+ }
57
+ }
58
+
59
+ private async fixNumericFieldsForSchema(schema: string) {
60
+ this.logger.log(`Processing numeric fields for schema: ${schema}`);
61
+
62
+ try {
63
+ // Get all tables with numeric columns in this schema
64
+ const numericColumnsQuery = `
65
+ SELECT
66
+ table_name,
67
+ column_name
68
+ FROM
69
+ information_schema.columns
70
+ WHERE
71
+ table_schema = $1
72
+ AND data_type = 'numeric'
73
+ `;
74
+
75
+ const numericColumnsResult = await this.targetPool.query(numericColumnsQuery, [schema]);
76
+
77
+ // Process each table with numeric columns
78
+ for (const row of numericColumnsResult.rows) {
79
+ const tableName = row.table_name;
80
+ const columnName = row.column_name;
81
+
82
+ this.logger.log(`Fixing numeric column ${columnName} in table ${schema}.${tableName}`);
83
+
84
+ // Update the column to cast text values to numeric
85
+ const updateQuery = `
86
+ UPDATE "${schema}"."${tableName}"
87
+ SET "${columnName}" = CASE
88
+ WHEN "${columnName}" IS NULL THEN NULL
89
+ WHEN "${columnName}" = '' THEN NULL
90
+ ELSE CAST("${columnName}" AS NUMERIC)
91
+ END
92
+ WHERE "${columnName}" IS NOT NULL
93
+ AND "${columnName}" != ''
94
+ AND "${columnName}" ~ '^[0-9]+(\.[0-9]+)?$';
95
+ `;
96
+
97
+ try {
98
+ await this.targetPool.query(updateQuery);
99
+ this.logger.log(`Successfully fixed numeric column ${columnName} in table ${schema}.${tableName}`);
100
+ } catch (updateError) {
101
+ this.logger.error(
102
+ `Error fixing numeric column ${columnName} in table ${schema}.${tableName}: ${updateError.message}`
103
+ );
104
+ }
105
+ }
106
+
107
+ this.logger.log(`Completed processing numeric fields for schema: ${schema}`);
108
+ } catch (error) {
109
+ this.logger.error(
110
+ `Error processing numeric fields for schema ${schema}: ${error.message}`,
111
+ error.stack
112
+ );
113
+ }
114
+ }
115
+
116
+ private async fixEnumFieldsForSchema(schema: string) {
117
+ this.logger.log(`Processing enum fields for schema: ${schema}`);
118
+
119
+ try {
120
+ // Get all tables with enum columns in this schema
121
+ const enumColumnsQuery = `
122
+ SELECT
123
+ c.table_name,
124
+ c.column_name,
125
+ c.udt_name
126
+ FROM
127
+ information_schema.columns c
128
+ WHERE
129
+ c.table_schema = $1
130
+ AND c.data_type = 'USER-DEFINED'
131
+ AND c.udt_name LIKE 'enum_%'
132
+ `;
133
+
134
+ const enumColumnsResult = await this.targetPool.query(enumColumnsQuery, [schema]);
135
+
136
+ // Process each table with enum columns
137
+ for (const row of enumColumnsResult.rows) {
138
+ const tableName = row.table_name;
139
+ const columnName = row.column_name;
140
+ const enumTypeName = row.udt_name;
141
+
142
+ this.logger.log(`Fixing enum column ${columnName} (${enumTypeName}) in table ${schema}.${tableName}`);
143
+
144
+ // Get the valid enum values
145
+ const enumValuesQuery = `
146
+ SELECT e.enumlabel
147
+ FROM pg_enum e
148
+ JOIN pg_type t ON e.enumtypid = t.oid
149
+ WHERE t.typname = $1
150
+ ORDER BY e.enumsortorder
151
+ `;
152
+
153
+ const enumValuesResult = await this.targetPool.query(enumValuesQuery, [enumTypeName]);
154
+ const validEnumValues = enumValuesResult.rows.map(r => r.enumlabel);
155
+
156
+ if (validEnumValues.length === 0) {
157
+ this.logger.warn(`No enum values found for type ${enumTypeName}. Skipping.`);
158
+ continue;
159
+ }
160
+
161
+ this.logger.log(`Valid values for ${enumTypeName}: ${validEnumValues.join(', ')}`);
162
+
163
+ // Get current values in the column
164
+ const currentValuesQuery = `
165
+ SELECT DISTINCT "${columnName}"
166
+ FROM "${schema}"."${tableName}"
167
+ WHERE "${columnName}" IS NOT NULL
168
+ `;
169
+
170
+ const currentValuesResult = await this.targetPool.query(currentValuesQuery);
171
+ const currentValues = currentValuesResult.rows.map(r => r[columnName]);
172
+
173
+ // Find invalid values
174
+ const invalidValues = currentValues.filter(val => !validEnumValues.includes(val));
175
+
176
+ if (invalidValues.length > 0) {
177
+ this.logger.log(`Found invalid values for enum ${enumTypeName}: ${invalidValues.join(', ')}`);
178
+
179
+ // For each invalid value, try to map it to a valid one or set to NULL
180
+ for (const invalidValue of invalidValues) {
181
+ // Try to find a case-insensitive match
182
+ const matchingValidValue = validEnumValues.find(
183
+ v => v.toLowerCase() === invalidValue.toLowerCase()
184
+ );
185
+
186
+ let updateQuery;
187
+ if (matchingValidValue) {
188
+ // If we found a case-insensitive match, use it
189
+ updateQuery = `
190
+ UPDATE "${schema}"."${tableName}"
191
+ SET "${columnName}" = $1
192
+ WHERE "${columnName}" = $2
193
+ `;
194
+ await this.targetPool.query(updateQuery, [matchingValidValue, invalidValue]);
195
+ this.logger.log(`Updated invalid value "${invalidValue}" to "${matchingValidValue}"`);
196
+ } else {
197
+ // Otherwise set to NULL or default value
198
+ updateQuery = `
199
+ UPDATE "${schema}"."${tableName}"
200
+ SET "${columnName}" = NULL
201
+ WHERE "${columnName}" = $1
202
+ `;
203
+ await this.targetPool.query(updateQuery, [invalidValue]);
204
+ this.logger.log(`Set invalid value "${invalidValue}" to NULL`);
205
+ }
206
+ }
207
+ } else {
208
+ this.logger.log(`No invalid enum values found for ${columnName} in ${schema}.${tableName}`);
209
+ }
210
+ }
211
+
212
+ this.logger.log(`Completed processing enum fields for schema: ${schema}`);
213
+ } catch (error) {
214
+ this.logger.error(
215
+ `Error processing enum fields for schema ${schema}: ${error.message}`,
216
+ error.stack
217
+ );
218
+ }
219
+ }
220
+
221
+ private async cleanup() {
222
+ this.logger.log("Cleaning up database connections");
223
+ await this.sourcePool.end();
224
+ await this.targetPool.end();
225
+ }
226
+ }
227
+
228
+ // Script para ejecutar desde línea de comandos
229
+ if (require.main === module) {
230
+ const run = async () => {
231
+ try {
232
+ const fixer = new DataTypeFixer();
233
+ await fixer.fixDataTypes();
234
+ process.exit(0);
235
+ } catch (error) {
236
+ console.error("Error:", error.message);
237
+ process.exit(1);
238
+ }
239
+ };
240
+
241
+ run();
242
242
  }