1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var tslib_1 = require("tslib");
|
4 | var Table_1 = require("../schema-builder/table/Table");
|
5 | var Migration_1 = require("./Migration");
|
6 | var PromiseUtils_1 = require("../util/PromiseUtils");
|
7 | var SqlServerDriver_1 = require("../driver/sqlserver/SqlServerDriver");
|
8 | var MssqlParameter_1 = require("../driver/sqlserver/MssqlParameter");
|
9 | var MongoDriver_1 = require("../driver/mongodb/MongoDriver");
|
10 |
|
11 |
|
12 |
|
13 | var MigrationExecutor = (function () {
|
14 |
|
15 |
|
16 |
|
17 | function MigrationExecutor(connection, queryRunner) {
|
18 | this.connection = connection;
|
19 | this.queryRunner = queryRunner;
|
20 |
|
21 |
|
22 |
|
23 | |
24 |
|
25 |
|
26 | this.transaction = true;
|
27 | var options = this.connection.driver.options;
|
28 | this.migrationsTableName = connection.options.migrationsTableName || "migrations";
|
29 | this.migrationsTable = this.connection.driver.buildTableName(this.migrationsTableName, options.schema, options.database);
|
30 | }
|
31 |
|
32 |
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 | MigrationExecutor.prototype.showMigrations = function () {
|
39 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
40 | var e_1, _a, hasUnappliedMigrations, queryRunner, executedMigrations, allMigrations, _loop_1, this_1, allMigrations_1, allMigrations_1_1, migration;
|
41 | return tslib_1.__generator(this, function (_b) {
|
42 | switch (_b.label) {
|
43 | case 0:
|
44 | hasUnappliedMigrations = false;
|
45 | queryRunner = this.queryRunner || this.connection.createQueryRunner("master");
|
46 |
|
47 | return [4 , this.createMigrationsTableIfNotExist(queryRunner)];
|
48 | case 1:
|
49 |
|
50 | _b.sent();
|
51 | return [4 , this.loadExecutedMigrations(queryRunner)];
|
52 | case 2:
|
53 | executedMigrations = _b.sent();
|
54 | allMigrations = this.getMigrations();
|
55 | _loop_1 = function (migration) {
|
56 | var executedMigration = executedMigrations.find(function (executedMigration) { return executedMigration.name === migration.name; });
|
57 | if (executedMigration) {
|
58 | this_1.connection.logger.logSchemaBuild(" [X] " + migration.name);
|
59 | }
|
60 | else {
|
61 | hasUnappliedMigrations = true;
|
62 | this_1.connection.logger.logSchemaBuild(" [ ] " + migration.name);
|
63 | }
|
64 | };
|
65 | this_1 = this;
|
66 | try {
|
67 | for (allMigrations_1 = tslib_1.__values(allMigrations), allMigrations_1_1 = allMigrations_1.next(); !allMigrations_1_1.done; allMigrations_1_1 = allMigrations_1.next()) {
|
68 | migration = allMigrations_1_1.value;
|
69 | _loop_1(migration);
|
70 | }
|
71 | }
|
72 | catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
73 | finally {
|
74 | try {
|
75 | if (allMigrations_1_1 && !allMigrations_1_1.done && (_a = allMigrations_1.return)) _a.call(allMigrations_1);
|
76 | }
|
77 | finally { if (e_1) throw e_1.error; }
|
78 | }
|
79 | if (!!this.queryRunner) return [3 , 4];
|
80 | return [4 , queryRunner.release()];
|
81 | case 3:
|
82 | _b.sent();
|
83 | _b.label = 4;
|
84 | case 4: return [2 , hasUnappliedMigrations];
|
85 | }
|
86 | });
|
87 | });
|
88 | };
|
89 | |
90 |
|
91 |
|
92 |
|
93 | MigrationExecutor.prototype.executePendingMigrations = function () {
|
94 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
95 | var queryRunner, executedMigrations, lastTimeExecutedMigration, allMigrations, successMigrations, pendingMigrations, transactionStartedByUs, err_1, rollbackError_1;
|
96 | var _this = this;
|
97 | return tslib_1.__generator(this, function (_a) {
|
98 | switch (_a.label) {
|
99 | case 0:
|
100 | queryRunner = this.queryRunner || this.connection.createQueryRunner("master");
|
101 |
|
102 | return [4 , this.createMigrationsTableIfNotExist(queryRunner)];
|
103 | case 1:
|
104 |
|
105 | _a.sent();
|
106 | return [4 , this.loadExecutedMigrations(queryRunner)];
|
107 | case 2:
|
108 | executedMigrations = _a.sent();
|
109 | lastTimeExecutedMigration = this.getLatestTimestampMigration(executedMigrations);
|
110 | allMigrations = this.getMigrations();
|
111 | successMigrations = [];
|
112 | pendingMigrations = allMigrations.filter(function (migration) {
|
113 |
|
114 | var executedMigration = executedMigrations.find(function (executedMigration) { return executedMigration.name === migration.name; });
|
115 | if (executedMigration)
|
116 | return false;
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | return true;
|
122 | });
|
123 | if (!!pendingMigrations.length) return [3 , 5];
|
124 | this.connection.logger.logSchemaBuild("No migrations are pending");
|
125 | if (!!this.queryRunner) return [3 , 4];
|
126 | return [4 , queryRunner.release()];
|
127 | case 3:
|
128 | _a.sent();
|
129 | _a.label = 4;
|
130 | case 4: return [2 , []];
|
131 | case 5:
|
132 |
|
133 | this.connection.logger.logSchemaBuild(executedMigrations.length + " migrations are already loaded in the database.");
|
134 | this.connection.logger.logSchemaBuild(allMigrations.length + " migrations were found in the source code.");
|
135 | if (lastTimeExecutedMigration)
|
136 | this.connection.logger.logSchemaBuild(lastTimeExecutedMigration.name + " is the last executed migration. It was executed on " + new Date(lastTimeExecutedMigration.timestamp).toString() + ".");
|
137 | this.connection.logger.logSchemaBuild(pendingMigrations.length + " migrations are new migrations that needs to be executed.");
|
138 | transactionStartedByUs = false;
|
139 | if (!(this.transaction && !queryRunner.isTransactionActive)) return [3 , 7];
|
140 | return [4 , queryRunner.startTransaction()];
|
141 | case 6:
|
142 | _a.sent();
|
143 | transactionStartedByUs = true;
|
144 | _a.label = 7;
|
145 | case 7:
|
146 | _a.trys.push([7, 11, 16, 19]);
|
147 | return [4 , PromiseUtils_1.PromiseUtils.runInSequence(pendingMigrations, function (migration) {
|
148 | return migration.instance.up(queryRunner)
|
149 | .then(function () {
|
150 | return _this.insertExecutedMigration(queryRunner, migration);
|
151 | })
|
152 | .then(function () {
|
153 | successMigrations.push(migration);
|
154 | _this.connection.logger.logSchemaBuild("Migration " + migration.name + " has been executed successfully.");
|
155 | });
|
156 | })];
|
157 | case 8:
|
158 | _a.sent();
|
159 | if (!transactionStartedByUs) return [3 , 10];
|
160 | return [4 , queryRunner.commitTransaction()];
|
161 | case 9:
|
162 | _a.sent();
|
163 | _a.label = 10;
|
164 | case 10: return [3 , 19];
|
165 | case 11:
|
166 | err_1 = _a.sent();
|
167 | if (!transactionStartedByUs) return [3 , 15];
|
168 | _a.label = 12;
|
169 | case 12:
|
170 | _a.trys.push([12, 14, , 15]);
|
171 | return [4 , queryRunner.rollbackTransaction()];
|
172 | case 13:
|
173 | _a.sent();
|
174 | return [3 , 15];
|
175 | case 14:
|
176 | rollbackError_1 = _a.sent();
|
177 | return [3 , 15];
|
178 | case 15: throw err_1;
|
179 | case 16:
|
180 | if (!!this.queryRunner) return [3 , 18];
|
181 | return [4 , queryRunner.release()];
|
182 | case 17:
|
183 | _a.sent();
|
184 | _a.label = 18;
|
185 | case 18: return [7 ];
|
186 | case 19: return [2 , successMigrations];
|
187 | }
|
188 | });
|
189 | });
|
190 | };
|
191 | |
192 |
|
193 |
|
194 | MigrationExecutor.prototype.undoLastMigration = function () {
|
195 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
196 | var queryRunner, executedMigrations, lastTimeExecutedMigration, allMigrations, migrationToRevert, transactionStartedByUs, err_2, rollbackError_2;
|
197 | return tslib_1.__generator(this, function (_a) {
|
198 | switch (_a.label) {
|
199 | case 0:
|
200 | queryRunner = this.queryRunner || this.connection.createQueryRunner("master");
|
201 |
|
202 | return [4 , this.createMigrationsTableIfNotExist(queryRunner)];
|
203 | case 1:
|
204 |
|
205 | _a.sent();
|
206 | return [4 , this.loadExecutedMigrations(queryRunner)];
|
207 | case 2:
|
208 | executedMigrations = _a.sent();
|
209 | lastTimeExecutedMigration = this.getLatestExecutedMigration(executedMigrations);
|
210 |
|
211 | if (!lastTimeExecutedMigration) {
|
212 | this.connection.logger.logSchemaBuild("No migrations was found in the database. Nothing to revert!");
|
213 | return [2 ];
|
214 | }
|
215 | allMigrations = this.getMigrations();
|
216 | migrationToRevert = allMigrations.find(function (migration) { return migration.name === lastTimeExecutedMigration.name; });
|
217 |
|
218 | if (!migrationToRevert)
|
219 | throw new Error("No migration " + lastTimeExecutedMigration.name + " was found in the source code. Make sure you have this migration in your codebase and its included in the connection options.");
|
220 |
|
221 | this.connection.logger.logSchemaBuild(executedMigrations.length + " migrations are already loaded in the database.");
|
222 | this.connection.logger.logSchemaBuild(lastTimeExecutedMigration.name + " is the last executed migration. It was executed on " + new Date(lastTimeExecutedMigration.timestamp).toString() + ".");
|
223 | this.connection.logger.logSchemaBuild("Now reverting it...");
|
224 | transactionStartedByUs = false;
|
225 | if (!(this.transaction && !queryRunner.isTransactionActive)) return [3 , 4];
|
226 | return [4 , queryRunner.startTransaction()];
|
227 | case 3:
|
228 | _a.sent();
|
229 | transactionStartedByUs = true;
|
230 | _a.label = 4;
|
231 | case 4:
|
232 | _a.trys.push([4, 9, 14, 17]);
|
233 | return [4 , migrationToRevert.instance.down(queryRunner)];
|
234 | case 5:
|
235 | _a.sent();
|
236 | return [4 , this.deleteExecutedMigration(queryRunner, migrationToRevert)];
|
237 | case 6:
|
238 | _a.sent();
|
239 | this.connection.logger.logSchemaBuild("Migration " + migrationToRevert.name + " has been reverted successfully.");
|
240 | if (!transactionStartedByUs) return [3 , 8];
|
241 | return [4 , queryRunner.commitTransaction()];
|
242 | case 7:
|
243 | _a.sent();
|
244 | _a.label = 8;
|
245 | case 8: return [3 , 17];
|
246 | case 9:
|
247 | err_2 = _a.sent();
|
248 | if (!transactionStartedByUs) return [3 , 13];
|
249 | _a.label = 10;
|
250 | case 10:
|
251 | _a.trys.push([10, 12, , 13]);
|
252 | return [4 , queryRunner.rollbackTransaction()];
|
253 | case 11:
|
254 | _a.sent();
|
255 | return [3 , 13];
|
256 | case 12:
|
257 | rollbackError_2 = _a.sent();
|
258 | return [3 , 13];
|
259 | case 13: throw err_2;
|
260 | case 14:
|
261 | if (!!this.queryRunner) return [3 , 16];
|
262 | return [4 , queryRunner.release()];
|
263 | case 15:
|
264 | _a.sent();
|
265 | _a.label = 16;
|
266 | case 16: return [7 ];
|
267 | case 17: return [2 ];
|
268 | }
|
269 | });
|
270 | });
|
271 | };
|
272 |
|
273 |
|
274 |
|
275 | |
276 |
|
277 |
|
278 | MigrationExecutor.prototype.createMigrationsTableIfNotExist = function (queryRunner) {
|
279 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
280 | var tableExist;
|
281 | return tslib_1.__generator(this, function (_a) {
|
282 | switch (_a.label) {
|
283 | case 0:
|
284 |
|
285 | if (this.connection.driver instanceof MongoDriver_1.MongoDriver) {
|
286 | return [2 ];
|
287 | }
|
288 | return [4 , queryRunner.hasTable(this.migrationsTable)];
|
289 | case 1:
|
290 | tableExist = _a.sent();
|
291 | if (!!tableExist) return [3 , 3];
|
292 | return [4 , queryRunner.createTable(new Table_1.Table({
|
293 | name: this.migrationsTable,
|
294 | columns: [
|
295 | {
|
296 | name: "id",
|
297 | type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationId }),
|
298 | isGenerated: true,
|
299 | generationStrategy: "increment",
|
300 | isPrimary: true,
|
301 | isNullable: false
|
302 | },
|
303 | {
|
304 | name: "timestamp",
|
305 | type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationTimestamp }),
|
306 | isPrimary: false,
|
307 | isNullable: false
|
308 | },
|
309 | {
|
310 | name: "name",
|
311 | type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationName }),
|
312 | isNullable: false
|
313 | },
|
314 | ]
|
315 | }))];
|
316 | case 2:
|
317 | _a.sent();
|
318 | _a.label = 3;
|
319 | case 3: return [2 ];
|
320 | }
|
321 | });
|
322 | });
|
323 | };
|
324 | |
325 |
|
326 |
|
327 | MigrationExecutor.prototype.loadExecutedMigrations = function (queryRunner) {
|
328 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
329 | var mongoRunner, migrationsRaw;
|
330 | return tslib_1.__generator(this, function (_a) {
|
331 | switch (_a.label) {
|
332 | case 0:
|
333 | if (!(this.connection.driver instanceof MongoDriver_1.MongoDriver)) return [3 , 2];
|
334 | mongoRunner = queryRunner;
|
335 | return [4 , mongoRunner.databaseConnection.db(this.connection.driver.database).collection(this.migrationsTableName).find().toArray()];
|
336 | case 1: return [2 , _a.sent()];
|
337 | case 2: return [4 , this.connection.manager
|
338 | .createQueryBuilder(queryRunner)
|
339 | .select()
|
340 | .from(this.migrationsTable, this.migrationsTableName)
|
341 | .getRawMany()];
|
342 | case 3:
|
343 | migrationsRaw = _a.sent();
|
344 | return [2 , migrationsRaw.map(function (migrationRaw) {
|
345 | return new Migration_1.Migration(parseInt(migrationRaw["id"]), parseInt(migrationRaw["timestamp"]), migrationRaw["name"]);
|
346 | })];
|
347 | }
|
348 | });
|
349 | });
|
350 | };
|
351 | |
352 |
|
353 |
|
354 | MigrationExecutor.prototype.getMigrations = function () {
|
355 | var migrations = this.connection.migrations.map(function (migration) {
|
356 | var migrationClassName = migration.constructor.name;
|
357 | var migrationTimestamp = parseInt(migrationClassName.substr(-13));
|
358 | if (!migrationTimestamp)
|
359 | throw new Error(migrationClassName + " migration name is wrong. Migration class name should have a JavaScript timestamp appended.");
|
360 | return new Migration_1.Migration(undefined, migrationTimestamp, migrationClassName, migration);
|
361 | });
|
362 |
|
363 | return migrations.sort(function (a, b) { return a.timestamp - b.timestamp; });
|
364 | };
|
365 | |
366 |
|
367 |
|
368 | MigrationExecutor.prototype.getLatestTimestampMigration = function (migrations) {
|
369 | var sortedMigrations = migrations.map(function (migration) { return migration; }).sort(function (a, b) { return (a.timestamp - b.timestamp) * -1; });
|
370 | return sortedMigrations.length > 0 ? sortedMigrations[0] : undefined;
|
371 | };
|
372 | |
373 |
|
374 |
|
375 | MigrationExecutor.prototype.getLatestExecutedMigration = function (migrations) {
|
376 | var sortedMigrations = migrations.map(function (migration) { return migration; }).sort(function (a, b) { return ((a.id || 0) - (b.id || 0)) * -1; });
|
377 | return sortedMigrations.length > 0 ? sortedMigrations[0] : undefined;
|
378 | };
|
379 | |
380 |
|
381 |
|
382 | MigrationExecutor.prototype.insertExecutedMigration = function (queryRunner, migration) {
|
383 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
384 | var values, mongoRunner, qb;
|
385 | return tslib_1.__generator(this, function (_a) {
|
386 | switch (_a.label) {
|
387 | case 0:
|
388 | values = {};
|
389 | if (this.connection.driver instanceof SqlServerDriver_1.SqlServerDriver) {
|
390 | values["timestamp"] = new MssqlParameter_1.MssqlParameter(migration.timestamp, this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationTimestamp }));
|
391 | values["name"] = new MssqlParameter_1.MssqlParameter(migration.name, this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationName }));
|
392 | }
|
393 | else {
|
394 | values["timestamp"] = migration.timestamp;
|
395 | values["name"] = migration.name;
|
396 | }
|
397 | if (!(this.connection.driver instanceof MongoDriver_1.MongoDriver)) return [3 , 1];
|
398 | mongoRunner = queryRunner;
|
399 | mongoRunner.databaseConnection.db(this.connection.driver.database).collection(this.migrationsTableName).insert(values);
|
400 | return [3 , 3];
|
401 | case 1:
|
402 | qb = queryRunner.manager.createQueryBuilder();
|
403 | return [4 , qb.insert()
|
404 | .into(this.migrationsTable)
|
405 | .values(values)
|
406 | .execute()];
|
407 | case 2:
|
408 | _a.sent();
|
409 | _a.label = 3;
|
410 | case 3: return [2 ];
|
411 | }
|
412 | });
|
413 | });
|
414 | };
|
415 | |
416 |
|
417 |
|
418 | MigrationExecutor.prototype.deleteExecutedMigration = function (queryRunner, migration) {
|
419 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
420 | var conditions, mongoRunner, qb;
|
421 | return tslib_1.__generator(this, function (_a) {
|
422 | switch (_a.label) {
|
423 | case 0:
|
424 | conditions = {};
|
425 | if (this.connection.driver instanceof SqlServerDriver_1.SqlServerDriver) {
|
426 | conditions["timestamp"] = new MssqlParameter_1.MssqlParameter(migration.timestamp, this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationTimestamp }));
|
427 | conditions["name"] = new MssqlParameter_1.MssqlParameter(migration.name, this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationName }));
|
428 | }
|
429 | else {
|
430 | conditions["timestamp"] = migration.timestamp;
|
431 | conditions["name"] = migration.name;
|
432 | }
|
433 | if (!(this.connection.driver instanceof MongoDriver_1.MongoDriver)) return [3 , 1];
|
434 | mongoRunner = queryRunner;
|
435 | mongoRunner.databaseConnection.db(this.connection.driver.database).collection(this.migrationsTableName).deleteOne(conditions);
|
436 | return [3 , 3];
|
437 | case 1:
|
438 | qb = queryRunner.manager.createQueryBuilder();
|
439 | return [4 , qb.delete()
|
440 | .from(this.migrationsTable)
|
441 | .where(qb.escape("timestamp") + " = :timestamp")
|
442 | .andWhere(qb.escape("name") + " = :name")
|
443 | .setParameters(conditions)
|
444 | .execute()];
|
445 | case 2:
|
446 | _a.sent();
|
447 | _a.label = 3;
|
448 | case 3: return [2 ];
|
449 | }
|
450 | });
|
451 | });
|
452 | };
|
453 | return MigrationExecutor;
|
454 | }());
|
455 | exports.MigrationExecutor = MigrationExecutor;
|
456 |
|
457 |
|