@aceitadev/adatabase 0.4.5 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Logger.js +15 -20
- package/dist/SchemaManager.d.ts +2 -0
- package/dist/SchemaManager.js +69 -22
- package/package.json +1 -1
package/dist/Logger.js
CHANGED
|
@@ -19,30 +19,25 @@ class Logger {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
static schemaLog(message, details) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
logOutput += `
|
|
31
|
-
|–– Table '${details.table}'`;
|
|
32
|
-
if (details.status) {
|
|
33
|
-
logOutput += ` (${details.status})`;
|
|
22
|
+
const PREFIX = `[32m[aDatabase][0m`;
|
|
23
|
+
if (details && details.table && !details.column) {
|
|
24
|
+
if (details.status === 'started') {
|
|
25
|
+
process.stdout.write(`${PREFIX} Synchronizing table '${details.table}'...`);
|
|
26
|
+
}
|
|
27
|
+
else if (details.status === 'created') {
|
|
28
|
+
process.stdout.write(` Done.
|
|
29
|
+
`);
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
|
-
if (details.column) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
\_ Column '${details.column}'`;
|
|
41
|
-
if (details.status) {
|
|
42
|
-
logOutput += ` (${details.status})`;
|
|
32
|
+
else if (details && details.table && details.column) {
|
|
33
|
+
if (details.status === 'added') {
|
|
34
|
+
console.log(`
|
|
35
|
+
${PREFIX} Column '${details.column}' added to table '${details.table}'.`);
|
|
43
36
|
}
|
|
44
37
|
}
|
|
45
|
-
|
|
38
|
+
else {
|
|
39
|
+
console.log(`${PREFIX} ${message}`);
|
|
40
|
+
}
|
|
46
41
|
}
|
|
47
42
|
}
|
|
48
43
|
exports.Logger = Logger;
|
package/dist/SchemaManager.d.ts
CHANGED
package/dist/SchemaManager.js
CHANGED
|
@@ -14,21 +14,21 @@ const Table_1 = require("./decorators/Table");
|
|
|
14
14
|
const Column_1 = require("./decorators/Column");
|
|
15
15
|
const Nullable_1 = require("./decorators/Nullable");
|
|
16
16
|
const Database_1 = require("./Database");
|
|
17
|
-
const Logger_1 = require("./Logger");
|
|
18
17
|
function camelToSnake(s) {
|
|
19
18
|
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
20
19
|
}
|
|
21
20
|
class SchemaManager {
|
|
22
21
|
constructor(models) {
|
|
22
|
+
this.changes = new Map();
|
|
23
23
|
this.models = models;
|
|
24
24
|
}
|
|
25
25
|
migrate() {
|
|
26
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
this.changes.clear();
|
|
27
28
|
for (const model of this.models) {
|
|
28
29
|
const table = (0, Table_1.getTableName)(model);
|
|
29
30
|
if (!table)
|
|
30
31
|
continue;
|
|
31
|
-
Logger_1.Logger.schemaLog("Synchronizing database schema...", { table: table, status: "started" });
|
|
32
32
|
const { columns, indexes } = this.getSchemaFromModel(model);
|
|
33
33
|
const existing = yield this.getExistingColumns(table);
|
|
34
34
|
if (Object.keys(existing).length === 0) {
|
|
@@ -38,6 +38,28 @@ class SchemaManager {
|
|
|
38
38
|
yield this.updateTable(table, columns, indexes, existing);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
this.printMigrationSummary();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
printMigrationSummary() {
|
|
45
|
+
if (this.changes.size === 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.log("Schema atualizado:");
|
|
49
|
+
const tableEntries = Array.from(this.changes.entries());
|
|
50
|
+
tableEntries.forEach(([table, changes], tableIndex) => {
|
|
51
|
+
const isLastTable = tableIndex === tableEntries.length - 1;
|
|
52
|
+
const tablePrefix = isLastTable ? "└─" : "├─";
|
|
53
|
+
if (tableIndex > 0) {
|
|
54
|
+
console.log("│");
|
|
55
|
+
}
|
|
56
|
+
console.log(`${tablePrefix} ${table}`);
|
|
57
|
+
changes.forEach((change, changeIndex) => {
|
|
58
|
+
const isLastChange = changeIndex === changes.length - 1;
|
|
59
|
+
const changeLinePrefix = isLastTable ? " " : "│ ";
|
|
60
|
+
const changeConnector = isLastChange ? "└─" : "├─";
|
|
61
|
+
console.log(` ${changeLinePrefix}${changeConnector} ${change}`);
|
|
62
|
+
});
|
|
41
63
|
});
|
|
42
64
|
}
|
|
43
65
|
createTable(table, columns, indexes) {
|
|
@@ -52,7 +74,8 @@ class SchemaManager {
|
|
|
52
74
|
const conn = yield (0, Database_1.getConnection)();
|
|
53
75
|
try {
|
|
54
76
|
yield (0, Database_1.execute)(sql, [], conn);
|
|
55
|
-
|
|
77
|
+
const columnChanges = Object.keys(columns).map(col => `+ ${col} (adicionado)`);
|
|
78
|
+
this.changes.set(table, columnChanges);
|
|
56
79
|
}
|
|
57
80
|
finally {
|
|
58
81
|
conn.release();
|
|
@@ -74,18 +97,34 @@ class SchemaManager {
|
|
|
74
97
|
}
|
|
75
98
|
updateTable(table, desired, indexes, existing) {
|
|
76
99
|
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
const tableChanges = [];
|
|
101
|
+
const conn = yield (0, Database_1.getConnection)();
|
|
102
|
+
try {
|
|
103
|
+
for (const [col, type] of Object.entries(desired)) {
|
|
104
|
+
if (!existing.hasOwnProperty(col)) {
|
|
105
|
+
const sql = `ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${type};`;
|
|
82
106
|
yield (0, Database_1.execute)(sql, [], conn);
|
|
83
|
-
|
|
107
|
+
tableChanges.push(`+ ${col} (adicionado)`);
|
|
84
108
|
}
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
else {
|
|
110
|
+
const normalize = (t) => t.toLowerCase().replace(/\s/g, '').replace('character varying', 'varchar');
|
|
111
|
+
const existingType = normalize(existing[col]);
|
|
112
|
+
const desiredType = normalize(type.split(' ')[0]);
|
|
113
|
+
if (existingType !== desiredType) {
|
|
114
|
+
// NOTE: Type modification is database-specific and can be risky.
|
|
115
|
+
// This implementation assumes MySQL's MODIFY COLUMN syntax.
|
|
116
|
+
const sql = `ALTER TABLE \`${table}\` MODIFY COLUMN \`${col}\` ${type};`;
|
|
117
|
+
yield (0, Database_1.execute)(sql, [], conn);
|
|
118
|
+
tableChanges.push(`~ ${col} (tipo alterado: ${existing[col].toUpperCase()} → ${type.split(' ')[0].toUpperCase()})`);
|
|
119
|
+
}
|
|
87
120
|
}
|
|
88
121
|
}
|
|
122
|
+
if (tableChanges.length > 0) {
|
|
123
|
+
this.changes.set(table, tableChanges);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
conn.release();
|
|
89
128
|
}
|
|
90
129
|
});
|
|
91
130
|
}
|
|
@@ -96,15 +135,14 @@ class SchemaManager {
|
|
|
96
135
|
const nullableMeta = (0, Nullable_1.getNullableMeta)(model);
|
|
97
136
|
const adapter = (0, Database_1.getAdapter)();
|
|
98
137
|
if (!colMeta) {
|
|
99
|
-
Logger_1.Logger.log(`No @Column or @Id decorators found on model ${model.name}.`, 'warn');
|
|
100
138
|
return { columns, indexes };
|
|
101
139
|
}
|
|
102
140
|
for (const [prop, opts] of colMeta.entries()) {
|
|
103
|
-
const colName = opts
|
|
141
|
+
const colName = (opts === null || opts === void 0 ? void 0 : opts.name) ? opts.name : camelToSnake(prop);
|
|
104
142
|
if (opts.index) {
|
|
105
143
|
indexes.push(colName);
|
|
106
144
|
}
|
|
107
|
-
if (opts
|
|
145
|
+
if (opts === null || opts === void 0 ? void 0 : opts.id) {
|
|
108
146
|
const type = opts.type || Number;
|
|
109
147
|
if (adapter.type === 'postgres') {
|
|
110
148
|
columns[colName] = "SERIAL PRIMARY KEY";
|
|
@@ -116,7 +154,6 @@ class SchemaManager {
|
|
|
116
154
|
}
|
|
117
155
|
const type = opts.type;
|
|
118
156
|
if (!type) {
|
|
119
|
-
Logger_1.Logger.log(`Type not provided for property '${prop}' on model '${model.name}'.`, 'error');
|
|
120
157
|
continue;
|
|
121
158
|
}
|
|
122
159
|
let sqlType = this.getSqlTypeForClass(type);
|
|
@@ -128,7 +165,7 @@ class SchemaManager {
|
|
|
128
165
|
sqlType = 'DECIMAL(10, 2)';
|
|
129
166
|
}
|
|
130
167
|
}
|
|
131
|
-
if (type === String && opts
|
|
168
|
+
else if (type === String && (opts === null || opts === void 0 ? void 0 : opts.limit)) {
|
|
132
169
|
sqlType = `VARCHAR(${opts.limit})`;
|
|
133
170
|
}
|
|
134
171
|
const isNullable = nullableMeta === null || nullableMeta === void 0 ? void 0 : nullableMeta.get(prop);
|
|
@@ -144,7 +181,7 @@ class SchemaManager {
|
|
|
144
181
|
sqlType += " DEFAULT CURRENT_TIMESTAMP";
|
|
145
182
|
}
|
|
146
183
|
}
|
|
147
|
-
if (opts
|
|
184
|
+
if (opts === null || opts === void 0 ? void 0 : opts.unique)
|
|
148
185
|
sqlType += " UNIQUE";
|
|
149
186
|
columns[colName] = sqlType;
|
|
150
187
|
}
|
|
@@ -177,25 +214,35 @@ class SchemaManager {
|
|
|
177
214
|
try {
|
|
178
215
|
let sql;
|
|
179
216
|
if (adapter.type === 'postgres') {
|
|
180
|
-
sql = `SELECT column_name,
|
|
217
|
+
sql = `SELECT column_name, udt_name || COALESCE('(' || character_maximum_length || ')', '') as column_type
|
|
218
|
+
FROM information_schema.columns
|
|
219
|
+
WHERE table_schema = current_schema() AND table_name = $1`;
|
|
181
220
|
}
|
|
182
221
|
else { // mysql
|
|
183
|
-
sql = `SELECT COLUMN_NAME,
|
|
222
|
+
sql = `SELECT COLUMN_NAME, COLUMN_TYPE
|
|
223
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
224
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`;
|
|
184
225
|
}
|
|
185
226
|
const rows = yield (0, Database_1.query)(sql, [table], conn);
|
|
227
|
+
if (rows.length === 0)
|
|
228
|
+
return {};
|
|
186
229
|
if (adapter.type === 'postgres') {
|
|
187
230
|
for (const r of rows) {
|
|
188
|
-
columns[r.column_name] = r.
|
|
231
|
+
columns[r.column_name] = r.column_type;
|
|
189
232
|
}
|
|
190
233
|
}
|
|
191
234
|
else {
|
|
192
235
|
for (const r of rows) {
|
|
193
|
-
columns[r.COLUMN_NAME] = r.
|
|
236
|
+
columns[r.COLUMN_NAME] = r.COLUMN_TYPE;
|
|
194
237
|
}
|
|
195
238
|
}
|
|
196
239
|
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
return {}; // Table likely does not exist
|
|
242
|
+
}
|
|
197
243
|
finally {
|
|
198
|
-
conn
|
|
244
|
+
if (conn)
|
|
245
|
+
conn.release();
|
|
199
246
|
}
|
|
200
247
|
return columns;
|
|
201
248
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aceitadev/adatabase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "Uma biblioteca para facilitar a interação com bancos de dados MySQL e PostgreSQL em projetos TypeScript/Node.js.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|