miqro 1.5.5 → 1.6.1
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 +42 -2
- package/dist/cli.js +8 -9
- package/dist/cli.js.map +1 -0
- package/dist/cmds/config-bash.js +1 -0
- package/dist/cmds/config-bash.js.map +1 -0
- package/dist/cmds/config-env.js +1 -0
- package/dist/cmds/config-env.js.map +1 -0
- package/dist/cmds/config-init.js +2 -0
- package/dist/cmds/config-init.js.map +1 -0
- package/dist/cmds/config.js +1 -0
- package/dist/cmds/config.js.map +1 -0
- package/dist/cmds/db-console.js +2 -0
- package/dist/cmds/db-console.js.map +1 -0
- package/dist/cmds/db-createmodel.js +4 -1
- package/dist/cmds/db-createmodel.js.map +1 -0
- package/dist/cmds/db-dump-data.js +2 -0
- package/dist/cmds/db-dump-data.js.map +1 -0
- package/dist/cmds/db-init.js +2 -0
- package/dist/cmds/db-init.js.map +1 -0
- package/dist/cmds/db-makemigrations.js +3 -0
- package/dist/cmds/db-makemigrations.js.map +1 -0
- package/dist/cmds/db-push-data.js +30 -4
- package/dist/cmds/db-push-data.js.map +1 -0
- package/dist/cmds/db-sync-makemigrations.js +3 -0
- package/dist/cmds/db-sync-makemigrations.js.map +1 -0
- package/dist/cmds/doc-json.js +1 -0
- package/dist/cmds/doc-json.js.map +1 -0
- package/dist/cmds/doc-md.js +53 -16
- package/dist/cmds/doc-md.js.map +1 -0
- package/dist/cmds/handler-apiroute-new.js +2 -36
- package/dist/cmds/handler-apiroute-new.js.map +1 -0
- package/dist/cmds/handler-main-new.d.ts +1 -2
- package/dist/cmds/handler-main-new.js +15 -22
- package/dist/cmds/handler-main-new.js.map +1 -0
- package/dist/cmds/new.d.ts +1 -3
- package/dist/cmds/new.js +8 -18
- package/dist/cmds/new.js.map +1 -0
- package/dist/cmds/start.js +4 -0
- package/dist/cmds/start.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/db/automigrations/deep-diff/index.d.ts +24 -0
- package/dist/utils/db/automigrations/deep-diff/index.js +240 -0
- package/dist/utils/db/automigrations/deep-diff/index.js.map +1 -0
- package/dist/utils/db/automigrations/index.js +17 -0
- package/dist/utils/db/automigrations/index.js.map +1 -0
- package/dist/utils/db/automigrations/migrate.js +136 -17
- package/dist/utils/db/automigrations/migrate.js.map +1 -0
- package/dist/utils/db/index.d.ts +3 -3
- package/dist/utils/db/index.js +11 -1
- package/dist/utils/db/index.js.map +1 -0
- package/dist/utils/doc/index.d.ts +10 -8
- package/dist/utils/doc/index.js +4 -1
- package/dist/utils/doc/index.js.map +1 -0
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/templates.d.ts +2 -2
- package/dist/utils/templates.js +6 -0
- package/dist/utils/templates.js.map +1 -0
- package/package.json +9 -15
- package/src/cli.ts +62 -0
- package/src/cmds/config-bash.ts +18 -0
- package/src/cmds/config-env.ts +18 -0
- package/src/cmds/config-init.ts +35 -0
- package/src/cmds/config.ts +16 -0
- package/src/cmds/db-console.ts +35 -0
- package/src/cmds/db-createmodel.ts +44 -0
- package/src/cmds/db-dump-data.ts +44 -0
- package/src/cmds/db-init.ts +42 -0
- package/src/cmds/db-makemigrations.ts +12 -0
- package/src/cmds/db-push-data.ts +77 -0
- package/src/cmds/db-sync-makemigrations.ts +12 -0
- package/src/cmds/doc-json.ts +16 -0
- package/src/cmds/doc-md.ts +233 -0
- package/src/cmds/handler-apiroute-new.ts +67 -0
- package/src/cmds/handler-main-new.ts +85 -0
- package/src/cmds/new.ts +128 -0
- package/src/cmds/start.ts +16 -0
- package/src/index.ts +2 -0
- package/src/utils/db/automigrations/deep-diff/index.ts +264 -0
- package/src/utils/db/automigrations/index.ts +151 -0
- package/src/utils/db/automigrations/migrate.ts +888 -0
- package/src/utils/db/index.ts +128 -0
- package/src/utils/doc/index.ts +31 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/templates.ts +189 -0
- package/tsconfig.json +32 -0
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
import { DEEPDIFF } from "./deep-diff";
|
|
6
|
+
|
|
7
|
+
interface TableMap {
|
|
8
|
+
[tableName: string]: {
|
|
9
|
+
tableName: string;
|
|
10
|
+
schema: any;
|
|
11
|
+
indexes: any;
|
|
12
|
+
model?: any;
|
|
13
|
+
options?: any;
|
|
14
|
+
fields?: any[];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MigrationState {
|
|
19
|
+
tables: TableMap;
|
|
20
|
+
revision: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* tslint:disable */
|
|
24
|
+
const reverseSequelizeColType = (DataTypes: any, col: any, prefix = "Sequelize."): string => {
|
|
25
|
+
const attrName = col.type.key;
|
|
26
|
+
const attrObj = col.type;
|
|
27
|
+
const options = (col.type.options) ? col.type.options : {};
|
|
28
|
+
|
|
29
|
+
// noinspection SpellCheckingInspection,DuplicateCaseLabelJS
|
|
30
|
+
switch (attrName) {
|
|
31
|
+
// CHAR(length, binary)
|
|
32
|
+
case DataTypes.CHAR.key:
|
|
33
|
+
if (options.binary) {
|
|
34
|
+
return prefix + "CHAR.BINARY";
|
|
35
|
+
}
|
|
36
|
+
return prefix + "CHAR(" + options.length + ")";
|
|
37
|
+
|
|
38
|
+
// STRING(length, binary).BINARY
|
|
39
|
+
case DataTypes.STRING.key:
|
|
40
|
+
return prefix + "STRING" + ((options.length) ? "(" + options.length + ")" : "") +
|
|
41
|
+
((options.binary) ? ".BINARY" : "");
|
|
42
|
+
|
|
43
|
+
// TEXT(length)
|
|
44
|
+
case DataTypes.TEXT.key:
|
|
45
|
+
if (!options.length) {
|
|
46
|
+
return prefix + "TEXT";
|
|
47
|
+
}
|
|
48
|
+
return prefix + "TEXT(" + options.length.toLowerCase() + ")";
|
|
49
|
+
|
|
50
|
+
// NUMBER(length, decimals).UNSIGNED.ZEROFILL
|
|
51
|
+
case DataTypes.NUMBER.key:
|
|
52
|
+
case DataTypes.TINYINT.key:
|
|
53
|
+
case DataTypes.SMALLINT.key:
|
|
54
|
+
case DataTypes.MEDIUMINT.key:
|
|
55
|
+
case DataTypes.BIGINT.key:
|
|
56
|
+
case DataTypes.FLOAT.key:
|
|
57
|
+
case DataTypes.REAL.key:
|
|
58
|
+
case DataTypes.DOUBLE.key:
|
|
59
|
+
case DataTypes.DECIMAL.key:
|
|
60
|
+
case DataTypes.INTEGER.key: {
|
|
61
|
+
let ret = attrName;
|
|
62
|
+
if (options.length) {
|
|
63
|
+
ret += "(" + options.length;
|
|
64
|
+
if (options.decimals) {
|
|
65
|
+
ret += ", " + options.decimals;
|
|
66
|
+
}
|
|
67
|
+
ret += ")";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (options.precision) {
|
|
71
|
+
ret += "(" + options.precision;
|
|
72
|
+
if (options.scale) {
|
|
73
|
+
ret += ", " + options.scale;
|
|
74
|
+
}
|
|
75
|
+
ret += ")";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ret = [ret];
|
|
79
|
+
|
|
80
|
+
if (options.zerofill) {
|
|
81
|
+
ret.push("ZEROFILL");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (options.unsigned) {
|
|
85
|
+
ret.push("UNSIGNED");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return prefix + ret.join(".");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case DataTypes.ENUM.key:
|
|
92
|
+
return prefix + "ENUM('" + options.values.join("', '") + "')";
|
|
93
|
+
|
|
94
|
+
case DataTypes.BLOB.key:
|
|
95
|
+
if (!options.length) {
|
|
96
|
+
return prefix + "BLOB";
|
|
97
|
+
}
|
|
98
|
+
return prefix + "BLOB(" + options.length.toLowerCase() + ")";
|
|
99
|
+
|
|
100
|
+
case DataTypes.GEOMETRY.key:
|
|
101
|
+
if (options.type) {
|
|
102
|
+
if (options.srid) {
|
|
103
|
+
return prefix + "GEOMETRY('" + options.type + "', " + options.srid + ")";
|
|
104
|
+
} else {
|
|
105
|
+
return prefix + "GEOMETRY('" + options.type + "')";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return prefix + "GEOMETRY";
|
|
109
|
+
|
|
110
|
+
case DataTypes.GEOGRAPHY.key:
|
|
111
|
+
return prefix + "GEOGRAPHY";
|
|
112
|
+
|
|
113
|
+
case DataTypes.ARRAY.key:
|
|
114
|
+
const _type = attrObj.toString();
|
|
115
|
+
let arrayType;
|
|
116
|
+
if (_type === "INTEGER[]" || _type === "STRING[]") {
|
|
117
|
+
arrayType = prefix + _type.replace("[]", "");
|
|
118
|
+
} else {
|
|
119
|
+
arrayType = (col.seqType === "Sequelize.ARRAY(Sequelize.INTEGER)") ? prefix + "INTEGER" : prefix + "STRING";
|
|
120
|
+
}
|
|
121
|
+
return prefix + `ARRAY(${arrayType})`;
|
|
122
|
+
|
|
123
|
+
case DataTypes.RANGE.key:
|
|
124
|
+
console.warn(attrName + " type not supported, you should make it by");
|
|
125
|
+
return prefix + attrObj.toSql();
|
|
126
|
+
|
|
127
|
+
// BOOLEAN
|
|
128
|
+
// TIME
|
|
129
|
+
// DATE
|
|
130
|
+
// DATEONLY
|
|
131
|
+
// HSTORE
|
|
132
|
+
// JSONB
|
|
133
|
+
// UUID
|
|
134
|
+
// UUIDV1
|
|
135
|
+
// UUIDV4
|
|
136
|
+
// VIRTUAL
|
|
137
|
+
// INET
|
|
138
|
+
// MACADDR
|
|
139
|
+
default:
|
|
140
|
+
return prefix + attrName;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const reverseSequelizeDefValueType = (defaultValue: any, prefix = "Sequelize."): any => {
|
|
145
|
+
if (typeof defaultValue === "object") {
|
|
146
|
+
if (defaultValue.constructor && defaultValue.constructor.name) {
|
|
147
|
+
return { internal: true, value: prefix + defaultValue.constructor.name };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (typeof defaultValue === "function") {
|
|
152
|
+
return { notSupported: true, value: "" };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { value: defaultValue };
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const parseIndex = (idx: any): any => {
|
|
159
|
+
delete idx.parser;
|
|
160
|
+
if (idx.type == "") {
|
|
161
|
+
delete idx.type;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const options: any = {};
|
|
165
|
+
|
|
166
|
+
if (idx.name) {
|
|
167
|
+
options.name = options.indexName = idx.name;
|
|
168
|
+
} // The name of the index. Default is __
|
|
169
|
+
|
|
170
|
+
// @todo: UNIQUE|FULLTEXT|SPATIAL
|
|
171
|
+
if (idx.unique) {
|
|
172
|
+
options.type = options.indicesType = "UNIQUE";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (idx.method) {
|
|
176
|
+
options.indexType = idx.type;
|
|
177
|
+
} // Set a type for the index, e.g. BTREE. See the documentation of the used dialect
|
|
178
|
+
|
|
179
|
+
if (idx.parser && idx.parser != "") {
|
|
180
|
+
options.parser = idx.parser;
|
|
181
|
+
} // For FULLTEXT columns set your parser
|
|
182
|
+
|
|
183
|
+
idx.options = options;
|
|
184
|
+
|
|
185
|
+
idx.hash = JSON.stringify(idx);
|
|
186
|
+
|
|
187
|
+
// log ('PI:', JSON.stringify(idx, null, 4));
|
|
188
|
+
return idx;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
192
|
+
export const reverseModels = (sequelizeModule: any, sequelize: any, realModels: { [model: string]: any }, logger: any): TableMap => {
|
|
193
|
+
|
|
194
|
+
const models: {
|
|
195
|
+
[key: string]: {
|
|
196
|
+
tableName: string;
|
|
197
|
+
options: any;
|
|
198
|
+
rawAttributes: { [attribute: string]: any };
|
|
199
|
+
};
|
|
200
|
+
} = {};
|
|
201
|
+
for (const model in realModels) {
|
|
202
|
+
if (model !== "default") {
|
|
203
|
+
models[model] = {
|
|
204
|
+
tableName: realModels[model].tableName,
|
|
205
|
+
options: {
|
|
206
|
+
...realModels[model].options
|
|
207
|
+
},
|
|
208
|
+
rawAttributes: {
|
|
209
|
+
...realModels[model].rawAttributes
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const tables: TableMap = {};
|
|
215
|
+
|
|
216
|
+
delete models.default;
|
|
217
|
+
|
|
218
|
+
for (const model in models) {
|
|
219
|
+
// noinspection JSUnfilteredForInLoop
|
|
220
|
+
const attributes = models[model].rawAttributes;
|
|
221
|
+
|
|
222
|
+
for (const column in attributes) {
|
|
223
|
+
// noinspection JSUnfilteredForInLoop
|
|
224
|
+
delete (attributes[column] as any).Model;
|
|
225
|
+
// noinspection JSUnfilteredForInLoop
|
|
226
|
+
delete (attributes[column] as any).fieldName;
|
|
227
|
+
// delete attributes[column].field;
|
|
228
|
+
|
|
229
|
+
// noinspection JSUnfilteredForInLoop
|
|
230
|
+
for (const property in attributes[column]) {
|
|
231
|
+
// noinspection JSUnfilteredForInLoop
|
|
232
|
+
if (property.startsWith("_")) {
|
|
233
|
+
// noinspection JSUnfilteredForInLoop
|
|
234
|
+
delete attributes[column][property];
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (property === "defaultValue") {
|
|
239
|
+
// noinspection JSUnfilteredForInLoop
|
|
240
|
+
const _val = reverseSequelizeDefValueType(attributes[column][property]);
|
|
241
|
+
if (_val.notSupported) {
|
|
242
|
+
// noinspection JSUnfilteredForInLoop
|
|
243
|
+
logger.info(`[Not supported] Skip defaultValue column of attribute ${model}:${column}`);
|
|
244
|
+
// noinspection JSUnfilteredForInLoop
|
|
245
|
+
delete attributes[column][property];
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
// noinspection JSUnfilteredForInLoop
|
|
249
|
+
attributes[column][property] = _val;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// remove getters, setters...
|
|
253
|
+
// noinspection JSUnfilteredForInLoop
|
|
254
|
+
if (typeof attributes[column][property] == "function") {
|
|
255
|
+
// noinspection JSUnfilteredForInLoop
|
|
256
|
+
delete attributes[column][property];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// noinspection JSUnfilteredForInLoop
|
|
260
|
+
if (typeof attributes[column].type === "undefined") {
|
|
261
|
+
// noinspection JSUnfilteredForInLoop
|
|
262
|
+
if (!(attributes[column] as any).seqType) {
|
|
263
|
+
// noinspection JSUnfilteredForInLoop
|
|
264
|
+
logger.info(`[Not supported] Skip column with undefined type ${model}:${column}`);
|
|
265
|
+
// noinspection JSUnfilteredForInLoop
|
|
266
|
+
delete attributes[column];
|
|
267
|
+
continue;
|
|
268
|
+
} else {
|
|
269
|
+
// noinspection JSUnfilteredForInLoop
|
|
270
|
+
if (!["Sequelize.ARRAY(Sequelize.INTEGER)", "Sequelize.ARRAY(Sequelize.STRING)"].includes((attributes[column] as any).seqType)) {
|
|
271
|
+
// noinspection JSUnfilteredForInLoop
|
|
272
|
+
delete attributes[column];
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
// noinspection JSUnfilteredForInLoop
|
|
276
|
+
(attributes[column] as any).type = {
|
|
277
|
+
key: sequelizeModule.ARRAY.key
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// noinspection JSUnfilteredForInLoop
|
|
282
|
+
let seqType = reverseSequelizeColType(sequelizeModule.DataTypes, attributes[column]);
|
|
283
|
+
|
|
284
|
+
// NO virtual types in migration
|
|
285
|
+
if (seqType === "Sequelize.VIRTUAL") {
|
|
286
|
+
// noinspection JSUnfilteredForInLoop
|
|
287
|
+
logger.info(`[SKIP] Skip Sequelize.VIRTUAL column "${column}"", defined in model "${model}"`);
|
|
288
|
+
// noinspection JSUnfilteredForInLoop
|
|
289
|
+
delete attributes[column];
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!seqType) {
|
|
294
|
+
// noinspection JSUnfilteredForInLoop
|
|
295
|
+
if (typeof (attributes[column] as any).type.options !== "undefined" && typeof (attributes[column] as any).type.options.toString === "function") {
|
|
296
|
+
// noinspection JSUnfilteredForInLoop
|
|
297
|
+
seqType = (attributes[column] as any).type.options.toString(sequelize);
|
|
298
|
+
}
|
|
299
|
+
// noinspection JSUnfilteredForInLoop
|
|
300
|
+
if (typeof attributes[column].type.toString === "function") {
|
|
301
|
+
// noinspection JSUnfilteredForInLoop
|
|
302
|
+
seqType = attributes[column].type.toString(sequelize);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// noinspection JSUnfilteredForInLoop
|
|
306
|
+
(attributes[column] as any).seqType = seqType;
|
|
307
|
+
// noinspection JSUnfilteredForInLoop
|
|
308
|
+
delete attributes[column].type;
|
|
309
|
+
// noinspection JSUnfilteredForInLoop
|
|
310
|
+
delete attributes[column].values; // ENUM
|
|
311
|
+
}
|
|
312
|
+
// noinspection JSUnfilteredForInLoop
|
|
313
|
+
(tables as any)[models[model].tableName] = {
|
|
314
|
+
// noinspection JSUnfilteredForInLoop
|
|
315
|
+
tableName: models[model].tableName,
|
|
316
|
+
schema: attributes
|
|
317
|
+
};
|
|
318
|
+
// noinspection JSUnfilteredForInLoop
|
|
319
|
+
if (models[model].options.indexes.length > 0) {
|
|
320
|
+
const idxOut = {};
|
|
321
|
+
// noinspection JSUnfilteredForInLoop
|
|
322
|
+
for (const _i in models[model].options.indexes) {
|
|
323
|
+
// noinspection JSUnfilteredForInLoop
|
|
324
|
+
const index = parseIndex(models[model].options.indexes[_i]);
|
|
325
|
+
(idxOut as any)[index.hash + ""] = index;
|
|
326
|
+
delete index.hash;
|
|
327
|
+
|
|
328
|
+
// make it immutable
|
|
329
|
+
Object.freeze(index);
|
|
330
|
+
}
|
|
331
|
+
// noinspection JSUnfilteredForInLoop
|
|
332
|
+
// models[model].options.indexes = idxOut;
|
|
333
|
+
tables[models[model].tableName].indexes = idxOut;
|
|
334
|
+
}
|
|
335
|
+
// noinspection JSUnfilteredForInLoop
|
|
336
|
+
if (typeof models[model].options.charset !== "undefined") {
|
|
337
|
+
// noinspection JSUnfilteredForInLoop
|
|
338
|
+
(tables as any)[models[model].tableName].charset = models[model].options.charset;
|
|
339
|
+
}
|
|
340
|
+
// noinspection JSUnfilteredForInLoop
|
|
341
|
+
(tables as any)[models[model].tableName].indexes = models[model].options.indexes;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return tables;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
interface DiffAction {
|
|
348
|
+
actionType: string;
|
|
349
|
+
columnName?: string;
|
|
350
|
+
attributeName?: any;
|
|
351
|
+
fields?: string[];
|
|
352
|
+
tableName?: any;
|
|
353
|
+
attributes?: any;
|
|
354
|
+
options?: any;
|
|
355
|
+
depends?: any;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export const parseDifference = (previousState: TableMap, currentState: TableMap, logger: any): DiffAction[] => {
|
|
359
|
+
// log(JSON.stringify(currentState, null, 4));
|
|
360
|
+
const actions: DiffAction[] = [];
|
|
361
|
+
const difference = DEEPDIFF.diff(previousState, currentState);
|
|
362
|
+
if (difference) {
|
|
363
|
+
for (const df of difference) {
|
|
364
|
+
// log (JSON.stringify(df, null, 4));
|
|
365
|
+
switch (df.kind) {
|
|
366
|
+
// add new
|
|
367
|
+
case "N": {
|
|
368
|
+
// new table created
|
|
369
|
+
if (df.path && df.path.length === 1) {
|
|
370
|
+
const depends: any[] = [];
|
|
371
|
+
const tableName = df.rhs.tableName;
|
|
372
|
+
if (df.rhs.schema) {
|
|
373
|
+
Object.keys(df.rhs.schema).forEach((v: any) => {
|
|
374
|
+
if (v.references) {
|
|
375
|
+
depends.push(v.references.model);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const options: any = {};
|
|
381
|
+
if (typeof df.rhs.charset !== "undefined") {
|
|
382
|
+
options.charset = df.rhs.charset;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
actions.push({
|
|
386
|
+
actionType: "createTable",
|
|
387
|
+
tableName,
|
|
388
|
+
attributes: df.rhs.schema,
|
|
389
|
+
options,
|
|
390
|
+
depends
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// create indexes
|
|
394
|
+
if (df.rhs.indexes) {
|
|
395
|
+
for (const _i in df.rhs.indexes) {
|
|
396
|
+
// noinspection JSUnfilteredForInLoop
|
|
397
|
+
actions.push({
|
|
398
|
+
...{
|
|
399
|
+
actionType: "addIndex",
|
|
400
|
+
tableName,
|
|
401
|
+
depends: [tableName]
|
|
402
|
+
}, ...{ ...df.rhs.indexes[_i] }
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const tableName = df.path && df.path[0];
|
|
410
|
+
const depends = [tableName];
|
|
411
|
+
|
|
412
|
+
if (df.path && df.path[1] === "schema") {
|
|
413
|
+
// if (df.path.length === 3) - new field
|
|
414
|
+
if (df.path.length === 3) {
|
|
415
|
+
// new field
|
|
416
|
+
if (df.rhs && df.rhs.references) {
|
|
417
|
+
depends.push(df.rhs.references.model);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
actions.push({
|
|
421
|
+
actionType: "addColumn",
|
|
422
|
+
tableName,
|
|
423
|
+
attributeName: df.path[2],
|
|
424
|
+
options: df.rhs,
|
|
425
|
+
depends
|
|
426
|
+
});
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// if (df.path.length > 3) - add new attribute to column (change col)
|
|
431
|
+
if (df.path && df.path.length > 3) {
|
|
432
|
+
if (df.path[1] === "schema") {
|
|
433
|
+
// new field attributes
|
|
434
|
+
const options = currentState[tableName as string].schema[df.path[2]];
|
|
435
|
+
if (options.references) {
|
|
436
|
+
depends.push(options.references.nodel);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
actions.push({
|
|
440
|
+
actionType: "changeColumn",
|
|
441
|
+
tableName,
|
|
442
|
+
attributeName: df.path[2],
|
|
443
|
+
options,
|
|
444
|
+
depends
|
|
445
|
+
});
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// new index
|
|
452
|
+
if (df.path && df.path[1] === "indexes") {
|
|
453
|
+
const tableName = df.path[0];
|
|
454
|
+
const index: DiffAction = {
|
|
455
|
+
...df.rhs,
|
|
456
|
+
actionType: "addIndex",
|
|
457
|
+
tableName: tableName,
|
|
458
|
+
depends: "addIndex",
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
index.depends = [tableName];
|
|
462
|
+
actions.push(index);
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
// drop
|
|
468
|
+
case "D": {
|
|
469
|
+
const tableName = df.path && df.path[0];
|
|
470
|
+
const depends = [tableName];
|
|
471
|
+
|
|
472
|
+
if (df.path && df.path.length === 1) {
|
|
473
|
+
// drop table
|
|
474
|
+
actions.push({
|
|
475
|
+
actionType: "dropTable",
|
|
476
|
+
tableName,
|
|
477
|
+
depends: []
|
|
478
|
+
});
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (df.path && df.path[1] === "schema") {
|
|
483
|
+
// if (df.path.length === 3) - drop field
|
|
484
|
+
if (df.path.length === 3) {
|
|
485
|
+
// drop column
|
|
486
|
+
actions.push({
|
|
487
|
+
actionType: "removeColumn",
|
|
488
|
+
tableName,
|
|
489
|
+
columnName: df.path[2],
|
|
490
|
+
depends: [tableName],
|
|
491
|
+
options: df.lhs
|
|
492
|
+
});
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// if (df.path.length > 3) - drop attribute from column (change col)
|
|
497
|
+
if (df.path.length > 3) {
|
|
498
|
+
// new field attributes
|
|
499
|
+
const options = currentState[tableName as string].schema[df.path[2]];
|
|
500
|
+
if (options.references) {
|
|
501
|
+
depends.push(options.references.nodel);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
actions.push({
|
|
505
|
+
actionType: "changeColumn",
|
|
506
|
+
tableName,
|
|
507
|
+
attributeName: df.path[2],
|
|
508
|
+
options,
|
|
509
|
+
depends
|
|
510
|
+
});
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (df.path && df.path[1] === "indexes") {
|
|
516
|
+
// log(df)
|
|
517
|
+
actions.push({
|
|
518
|
+
actionType: "removeIndex",
|
|
519
|
+
tableName,
|
|
520
|
+
fields: df.lhs.fields as any,
|
|
521
|
+
options: df.lhs.options,
|
|
522
|
+
depends: [tableName]
|
|
523
|
+
});
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
// edit
|
|
529
|
+
case "E": {
|
|
530
|
+
const tableName = df.path && df.path[0];
|
|
531
|
+
const depends = [tableName];
|
|
532
|
+
|
|
533
|
+
if (df.path && df.path[1] === "schema") {
|
|
534
|
+
// new field attributes
|
|
535
|
+
const options = currentState[tableName as string].schema[df.path[2]];
|
|
536
|
+
if (options.references) {
|
|
537
|
+
depends.push(options.references.nodel);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
actions.push({
|
|
541
|
+
actionType: "changeColumn",
|
|
542
|
+
tableName,
|
|
543
|
+
attributeName: df.path[2],
|
|
544
|
+
options,
|
|
545
|
+
depends
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// updated index
|
|
550
|
+
// only support updating and dropping indexes
|
|
551
|
+
if (df.path && df.path[1] === "indexes") {
|
|
552
|
+
const tableName = df.path && df.path[0];
|
|
553
|
+
let keys = Object.keys(df.rhs);
|
|
554
|
+
|
|
555
|
+
// noinspection LoopStatementThatDoesntLoopJS
|
|
556
|
+
for (const k in keys) {
|
|
557
|
+
const key = keys[k];
|
|
558
|
+
// noinspection JSUnusedLocalSymbols
|
|
559
|
+
actions.push({
|
|
560
|
+
actionType: "addIndex",
|
|
561
|
+
tableName,
|
|
562
|
+
fields: df.rhs[key].fields,
|
|
563
|
+
options: df.rhs[key].options,
|
|
564
|
+
depends: [tableName]
|
|
565
|
+
});
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
keys = Object.keys(df.lhs);
|
|
570
|
+
// noinspection LoopStatementThatDoesntLoopJS
|
|
571
|
+
for (const k in keys) {
|
|
572
|
+
const key = keys[k];
|
|
573
|
+
// noinspection JSUnusedLocalSymbols
|
|
574
|
+
actions.push({
|
|
575
|
+
actionType: "removeIndex",
|
|
576
|
+
tableName,
|
|
577
|
+
fields: df.lhs[key].fields,
|
|
578
|
+
options: df.lhs[key].options,
|
|
579
|
+
depends: [tableName]
|
|
580
|
+
});
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
// array change indexes
|
|
587
|
+
case "A": {
|
|
588
|
+
logger.info("[Not supported] Array model changes! Problems are possible. Please, check result more carefully!");
|
|
589
|
+
logger.info("[Not supported] Difference: ");
|
|
590
|
+
logger.info(JSON.stringify(df, null, 4));
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
default:
|
|
594
|
+
// code
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return actions;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
export const sortActions = (actions: DiffAction[]): void => {
|
|
603
|
+
const orderedActionTypes = [
|
|
604
|
+
"removeIndex",
|
|
605
|
+
"removeColumn",
|
|
606
|
+
"dropTable",
|
|
607
|
+
"createTable",
|
|
608
|
+
"addColumn",
|
|
609
|
+
"changeColumn",
|
|
610
|
+
"addIndex"
|
|
611
|
+
];
|
|
612
|
+
|
|
613
|
+
// test
|
|
614
|
+
// actions = shuffleArray(actions);
|
|
615
|
+
|
|
616
|
+
actions.sort((a, b) => {
|
|
617
|
+
if (orderedActionTypes.indexOf(a.actionType) < orderedActionTypes.indexOf(b.actionType)) {
|
|
618
|
+
return -1;
|
|
619
|
+
}
|
|
620
|
+
if (orderedActionTypes.indexOf(a.actionType) > orderedActionTypes.indexOf(b.actionType)) {
|
|
621
|
+
return 1;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (a.depends.length === 0 && b.depends.length > 0) {
|
|
625
|
+
return -1;
|
|
626
|
+
} // a < b
|
|
627
|
+
if (b.depends.length === 0 && a.depends.length > 0) {
|
|
628
|
+
return 1;
|
|
629
|
+
} // b < a
|
|
630
|
+
|
|
631
|
+
return 0;
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
for (let k = 0; k <= actions.length; k++) {
|
|
635
|
+
for (let i = 0; i < actions.length; i++) {
|
|
636
|
+
if (!actions[i].depends) {
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (actions[i].depends.length === 0) {
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const a = actions[i];
|
|
644
|
+
|
|
645
|
+
for (let j = 0; j < actions.length; j++) {
|
|
646
|
+
if (!actions[j].depends) {
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
if (actions[j].depends.length === 0) {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const b = actions[j];
|
|
654
|
+
|
|
655
|
+
if (a.actionType != b.actionType) {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (b.depends.indexOf(a.tableName) !== -1 && i > j) {
|
|
660
|
+
const c = actions[i];
|
|
661
|
+
actions[i] = actions[j];
|
|
662
|
+
actions[j] = c;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
interface Migration {
|
|
671
|
+
commandsUp: string[];
|
|
672
|
+
consoleOut: string[];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// noinspection SpellCheckingInspection
|
|
676
|
+
export const getMigration = (actions: DiffAction[]): Migration => {
|
|
677
|
+
const propertyToStr = (obj: any): any => {
|
|
678
|
+
// noinspection SpellCheckingInspection
|
|
679
|
+
const vals = [];
|
|
680
|
+
for (const k in obj) {
|
|
681
|
+
if (k === "seqType") {
|
|
682
|
+
// noinspection JSUnfilteredForInLoop
|
|
683
|
+
vals.push('"type": ' + obj[k]);
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (k === "defaultValue") {
|
|
688
|
+
// noinspection JSUnfilteredForInLoop
|
|
689
|
+
if (obj[k].internal) {
|
|
690
|
+
// noinspection JSUnfilteredForInLoop
|
|
691
|
+
vals.push('"defaultValue": ' + obj[k].value);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
// noinspection JSUnfilteredForInLoop
|
|
695
|
+
if (obj[k].notSupported) {
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const x = {};
|
|
700
|
+
// noinspection JSUnfilteredForInLoop
|
|
701
|
+
(x as any)[k] = obj[k].value;
|
|
702
|
+
vals.push(JSON.stringify(x).slice(1, -1));
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const x = {};
|
|
707
|
+
// noinspection JSUnfilteredForInLoop
|
|
708
|
+
(x as any)[k] = obj[k];
|
|
709
|
+
vals.push(JSON.stringify(x).slice(1, -1));
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return "{ " + vals.reverse().join(", ") + " }";
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
const getAttributes = (attrs: any[]): any => {
|
|
716
|
+
const ret = [];
|
|
717
|
+
for (const attrName in attrs) {
|
|
718
|
+
// noinspection JSUnfilteredForInLoop
|
|
719
|
+
ret.push(` "${attrName}": ${propertyToStr(attrs[attrName])}`);
|
|
720
|
+
}
|
|
721
|
+
return " { \n" + ret.join(", \n") + "\n }";
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
const commandsUp = [];
|
|
725
|
+
const consoleOut = [];
|
|
726
|
+
|
|
727
|
+
for (const _i in actions) {
|
|
728
|
+
// noinspection JSUnfilteredForInLoop
|
|
729
|
+
const action = actions[_i];
|
|
730
|
+
// noinspection FallThroughInSwitchStatementJS,FallThroughInSwitchStatementJS
|
|
731
|
+
switch (action.actionType) {
|
|
732
|
+
case "createTable": {
|
|
733
|
+
const resUp = `{ fn: "createTable", params: [
|
|
734
|
+
"${action.tableName}",
|
|
735
|
+
${getAttributes(action.attributes)},
|
|
736
|
+
${JSON.stringify(action.options)}
|
|
737
|
+
] }`;
|
|
738
|
+
commandsUp.push(resUp);
|
|
739
|
+
|
|
740
|
+
consoleOut.push(`createTable "${action.tableName}", deps: [${action.depends.join(", ")}]`);
|
|
741
|
+
}
|
|
742
|
+
break;
|
|
743
|
+
|
|
744
|
+
case "dropTable": {
|
|
745
|
+
const res = `{ fn: "dropTable", params: ["${action.tableName}"] }`;
|
|
746
|
+
commandsUp.push(res);
|
|
747
|
+
|
|
748
|
+
consoleOut.push(`dropTable "${action.tableName}"`);
|
|
749
|
+
}
|
|
750
|
+
break;
|
|
751
|
+
|
|
752
|
+
case "addColumn": {
|
|
753
|
+
const resUp = `{ fn: "addColumn", params: [
|
|
754
|
+
"${action.tableName}",
|
|
755
|
+
"${(action.options && action.options.field) ? action.options.field : action.attributeName}",
|
|
756
|
+
${propertyToStr(action.options)}
|
|
757
|
+
] }`;
|
|
758
|
+
|
|
759
|
+
commandsUp.push(resUp);
|
|
760
|
+
|
|
761
|
+
consoleOut.push(`addColumn "${action.attributeName}" to table "${action.tableName}"`);
|
|
762
|
+
}
|
|
763
|
+
break;
|
|
764
|
+
|
|
765
|
+
case "removeColumn": {
|
|
766
|
+
const res = `{ fn: "removeColumn", params: ["${action.tableName}", "${(action.options && action.options.field) ? action.options.field : action.columnName}"] }`;
|
|
767
|
+
commandsUp.push(res);
|
|
768
|
+
|
|
769
|
+
consoleOut.push(`removeColumn "${(action.options && action.options.field) ? action.options.field : action.columnName}" from table "${action.tableName}"`);
|
|
770
|
+
}
|
|
771
|
+
break;
|
|
772
|
+
|
|
773
|
+
case "changeColumn": {
|
|
774
|
+
const res = `{ fn: "changeColumn", params: [
|
|
775
|
+
"${action.tableName}",
|
|
776
|
+
"${(action.options && action.options.field) ? action.options.field : action.attributeName}",
|
|
777
|
+
${propertyToStr(action.options)}
|
|
778
|
+
] }`;
|
|
779
|
+
commandsUp.push(res);
|
|
780
|
+
|
|
781
|
+
consoleOut.push(`changeColumn "${action.attributeName}" on table "${action.tableName}"`);
|
|
782
|
+
}
|
|
783
|
+
break;
|
|
784
|
+
|
|
785
|
+
case "addIndex": {
|
|
786
|
+
const res = `{ fn: "addIndex", params: [
|
|
787
|
+
"${action.tableName}",
|
|
788
|
+
${JSON.stringify(action.fields)},
|
|
789
|
+
${JSON.stringify(action.options)}
|
|
790
|
+
] }`;
|
|
791
|
+
commandsUp.push(res);
|
|
792
|
+
|
|
793
|
+
const nameOrAttrs = (action.options && action.options.indexName && action.options.indexName != "") ? `"${action.options.indexName}"` : JSON.stringify(action.fields);
|
|
794
|
+
consoleOut.push(`addIndex ${nameOrAttrs} to table "${action.tableName}"`);
|
|
795
|
+
}
|
|
796
|
+
break;
|
|
797
|
+
|
|
798
|
+
case "removeIndex": {
|
|
799
|
+
// log(action)
|
|
800
|
+
const nameOrAttrs = (action.options && action.options.indexName && action.options.indexName != "") ? `"${action.options.indexName}"` : JSON.stringify(action.fields);
|
|
801
|
+
|
|
802
|
+
const res = `{ fn: "removeIndex", params: [
|
|
803
|
+
"${action.tableName}",
|
|
804
|
+
${nameOrAttrs}
|
|
805
|
+
] }`;
|
|
806
|
+
commandsUp.push(res);
|
|
807
|
+
|
|
808
|
+
consoleOut.push(`removeIndex ${nameOrAttrs} from table "${action.tableName}"`);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
default:
|
|
812
|
+
// code
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return { commandsUp, consoleOut };
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
interface WritenMigration {
|
|
820
|
+
filename: string;
|
|
821
|
+
info: {
|
|
822
|
+
revision: number;
|
|
823
|
+
name: string;
|
|
824
|
+
created: Date;
|
|
825
|
+
comment: string;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
export const writeMigration = (revision: number, migration: Migration, migrationsDir: string, name = "", comment = ""): WritenMigration => {
|
|
830
|
+
const _commands = "var migrationCommands = [ \n" + migration.commandsUp.join(", \n") + " \n];\n";
|
|
831
|
+
const _actions = " * " + migration.consoleOut.join("\n * ");
|
|
832
|
+
|
|
833
|
+
//_commands = js_beautify(_commands);
|
|
834
|
+
const info = {
|
|
835
|
+
revision,
|
|
836
|
+
name,
|
|
837
|
+
created: new Date(),
|
|
838
|
+
comment
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
const template = `'use strict';
|
|
842
|
+
|
|
843
|
+
const Sequelize = require('sequelize');
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Actions summary:
|
|
847
|
+
*
|
|
848
|
+
${_actions}
|
|
849
|
+
*
|
|
850
|
+
**/
|
|
851
|
+
|
|
852
|
+
const info = ${JSON.stringify(info, null, 4)};
|
|
853
|
+
|
|
854
|
+
${_commands}
|
|
855
|
+
|
|
856
|
+
module.exports = {
|
|
857
|
+
pos: 0,
|
|
858
|
+
up: function(queryInterface, Sequelize)
|
|
859
|
+
{
|
|
860
|
+
var index = this.pos;
|
|
861
|
+
return new Promise(function(resolve, reject) {
|
|
862
|
+
function next() {
|
|
863
|
+
if (index < migrationCommands.length)
|
|
864
|
+
{
|
|
865
|
+
let command = migrationCommands[index];
|
|
866
|
+
console.log("[#"+index+"] execute: " + command.fn);
|
|
867
|
+
index++;
|
|
868
|
+
queryInterface[command.fn].apply(queryInterface, command.params).then(next, reject);
|
|
869
|
+
}
|
|
870
|
+
else
|
|
871
|
+
resolve();
|
|
872
|
+
}
|
|
873
|
+
next();
|
|
874
|
+
});
|
|
875
|
+
},
|
|
876
|
+
info: info
|
|
877
|
+
};
|
|
878
|
+
`;
|
|
879
|
+
|
|
880
|
+
name = name.replace(" ", "_");
|
|
881
|
+
const now = new Date();
|
|
882
|
+
const timestamp = `${now.getFullYear()}${now.getMonth() + 1 < 10 ? "0" : ""}${now.getMonth() + 1}${now.getDate() < 10 ? "0" : ""}${now.getDate()}${now.getHours() < 10 ? "0" : ""}${now.getHours()}${now.getMinutes() < 10 ? "0" : ""}${now.getMinutes()}${now.getSeconds() < 10 ? "0" : ""}${now.getSeconds()}-${revision}`;
|
|
883
|
+
const filename = path.join(migrationsDir, timestamp + ((name != "") ? `-${name}` : "") + ".js");
|
|
884
|
+
|
|
885
|
+
fs.writeFileSync(filename, template);
|
|
886
|
+
|
|
887
|
+
return { filename, info };
|
|
888
|
+
};
|