@oliasoft-open-source/node-json-migrator 2.3.10 → 3.0.0-beta-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/dist/index.cjs +16553 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.mts +81 -0
- package/dist/index.mjs +16526 -0
- package/package.json +51 -57
- package/dist/create/create.js +0 -76
- package/dist/database/database.js +0 -102
- package/dist/executed/executed.js +0 -68
- package/dist/git/git.js +0 -130
- package/dist/glob/glob.js +0 -58
- package/dist/hash/hash.js +0 -16
- package/dist/history/history.js +0 -90
- package/dist/index.js +0 -54
- package/dist/migrate/migrate.js +0 -59
- package/dist/pending/pending.js +0 -73
- package/dist/pipe/pipe.js +0 -38
- package/dist/plan/cached-planned-version.js +0 -25
- package/dist/plan/plan.js +0 -274
- package/dist/plan/plan.schema.json +0 -20
- package/dist/plan/planned-version.js +0 -26
- package/dist/plan/validator.js +0 -216
package/dist/history/history.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.printVersionHistory = void 0;
|
|
7
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
-
var _git = require("../git/git");
|
|
9
|
-
var _database = require("../database/database");
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
/**
|
|
12
|
-
* Parse plan.json
|
|
13
|
-
*
|
|
14
|
-
* @param {string} plan
|
|
15
|
-
* @returns object|null parsed plan
|
|
16
|
-
*/
|
|
17
|
-
const parsePlan = plan => {
|
|
18
|
-
return (() => {
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(plan);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
console.error('Unable to parse plan.json');
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
})();
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @typedef {Object} Configuration
|
|
30
|
-
* @property {string} directory (path to migrations directory)
|
|
31
|
-
* @property {string} entity (entity name for payload, e.g. dataset)
|
|
32
|
-
* @property {Object} database (connected pg-promise database)
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Prints the version history of plan.json (for debugging)
|
|
37
|
-
*
|
|
38
|
-
* @param {Object} args
|
|
39
|
-
* @param {Configuration} args.config
|
|
40
|
-
*/
|
|
41
|
-
const printVersionHistory = async ({
|
|
42
|
-
config
|
|
43
|
-
}) => {
|
|
44
|
-
const {
|
|
45
|
-
database,
|
|
46
|
-
entity,
|
|
47
|
-
directory
|
|
48
|
-
} = config;
|
|
49
|
-
try {
|
|
50
|
-
const planFilePath = `${directory}/plan.json`;
|
|
51
|
-
|
|
52
|
-
//read version history from database cache
|
|
53
|
-
const versionHistoryFromDatabase = await (0, _database.getVersions)(database, entity);
|
|
54
|
-
const fullDatabaseVersionHistory = versionHistoryFromDatabase.map(d => {
|
|
55
|
-
const plan = parsePlan(d?.plan);
|
|
56
|
-
const finalMigration = plan?.length ? plan[plan.length - 1] : null;
|
|
57
|
-
return {
|
|
58
|
-
planVersion: d?.version,
|
|
59
|
-
lastFileName: finalMigration?.fileName || null,
|
|
60
|
-
lastSequence: finalMigration?.sequence || null
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
//read full version history from git logs (back to beginning of time)
|
|
65
|
-
const {
|
|
66
|
-
historicalPlans: fullHistoricalPlans
|
|
67
|
-
} = await (0, _git.getHistoricalPlansFromGit)(planFilePath);
|
|
68
|
-
const fullGitVersionHistory = fullHistoricalPlans.map(p => {
|
|
69
|
-
const plan = parsePlan(p?.plan);
|
|
70
|
-
const finalMigration = plan?.length ? plan[plan.length - 1] : null;
|
|
71
|
-
return {
|
|
72
|
-
gitCommitHash: p?.revision?.version,
|
|
73
|
-
planVersion: p?.version,
|
|
74
|
-
timeStamp: p?.revision?.timeStamp,
|
|
75
|
-
lastFileName: finalMigration?.fileName || null,
|
|
76
|
-
lastSequence: finalMigration?.sequence || null
|
|
77
|
-
};
|
|
78
|
-
});
|
|
79
|
-
const output = {
|
|
80
|
-
fullDatabaseVersionHistory,
|
|
81
|
-
fullGitVersionHistory
|
|
82
|
-
};
|
|
83
|
-
console.log(JSON.stringify(output, null, 2));
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(_chalk.default.red(error));
|
|
86
|
-
console.error(_chalk.default.red(_git.historyError));
|
|
87
|
-
throw new Error('Unable to print debug history');
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
exports.printVersionHistory = printVersionHistory;
|
package/dist/index.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
Object.defineProperty(exports, "createMigration", {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: function () {
|
|
9
|
-
return _create.createMigration;
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
Object.defineProperty(exports, "getPlannedMigrations", {
|
|
13
|
-
enumerable: true,
|
|
14
|
-
get: function () {
|
|
15
|
-
return _plan.getPlannedMigrations;
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
Object.defineProperty(exports, "getPlannedVersion", {
|
|
19
|
-
enumerable: true,
|
|
20
|
-
get: function () {
|
|
21
|
-
return _plannedVersion.getPlannedVersion;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
Object.defineProperty(exports, "getVersions", {
|
|
25
|
-
enumerable: true,
|
|
26
|
-
get: function () {
|
|
27
|
-
return _database.getVersions;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
Object.defineProperty(exports, "migrate", {
|
|
31
|
-
enumerable: true,
|
|
32
|
-
get: function () {
|
|
33
|
-
return _migrate.migrate;
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
Object.defineProperty(exports, "pipe", {
|
|
37
|
-
enumerable: true,
|
|
38
|
-
get: function () {
|
|
39
|
-
return _pipe.pipe;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
Object.defineProperty(exports, "printVersionHistory", {
|
|
43
|
-
enumerable: true,
|
|
44
|
-
get: function () {
|
|
45
|
-
return _history.printVersionHistory;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
var _create = require("./create/create");
|
|
49
|
-
var _plan = require("./plan/plan");
|
|
50
|
-
var _plannedVersion = require("./plan/planned-version");
|
|
51
|
-
var _migrate = require("./migrate/migrate");
|
|
52
|
-
var _pipe = require("./pipe/pipe");
|
|
53
|
-
var _database = require("./database/database");
|
|
54
|
-
var _history = require("./history/history");
|
package/dist/migrate/migrate.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.migrate = void 0;
|
|
7
|
-
var _pipe = require("../pipe/pipe");
|
|
8
|
-
var _pending = require("../pending/pending");
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Array|Object} Payload
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {Object} PlannedMigrations
|
|
15
|
-
* @property {Array<function>} plannedMigrations (planned migrations)
|
|
16
|
-
* @property {string} nextVersion (next payload version)
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} Configuration
|
|
21
|
-
* @property {string} directory (path to migrations directory)
|
|
22
|
-
* @property {string} entity (entity name for payload, e.g. dataset)
|
|
23
|
-
* @property {string} [version] (current payload version)
|
|
24
|
-
* @property {boolean} [force] (override validation)
|
|
25
|
-
* @property {boolean} [dry] (don't alter database or files)
|
|
26
|
-
* @property {Object} [database] (connected pg-promise database)
|
|
27
|
-
* @property {Object} [pgpHelpers] (pg-promise helpers)
|
|
28
|
-
* @property {PlannedMigrations} [plan]
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Migrates a payload
|
|
33
|
-
*
|
|
34
|
-
* There are two ways to use this function:
|
|
35
|
-
* i) function looks up the pending migrations for you (default)
|
|
36
|
-
* ii) pass in pre-fetched pending migrators via config.pending
|
|
37
|
-
*
|
|
38
|
-
* @param {Object} args
|
|
39
|
-
* @param {Payload} args.payload
|
|
40
|
-
* @param {Configuration} args.config
|
|
41
|
-
* @returns Promise<{nextPayload: Payload, nextVersion: string}>
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
const migrate = async ({
|
|
45
|
-
payload,
|
|
46
|
-
config
|
|
47
|
-
}) => {
|
|
48
|
-
const {
|
|
49
|
-
pendingMigrators,
|
|
50
|
-
nextVersion
|
|
51
|
-
} = await (0, _pending.getPendingMigrators)({
|
|
52
|
-
config
|
|
53
|
-
});
|
|
54
|
-
return {
|
|
55
|
-
nextPayload: pendingMigrators.length ? (0, _pipe.pipe)(pendingMigrators, payload) : payload,
|
|
56
|
-
nextVersion
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
exports.migrate = migrate;
|
package/dist/pending/pending.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getPendingMigrators = exports.getPendingMigrations = void 0;
|
|
7
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
-
var _plan = require("../plan/plan");
|
|
9
|
-
var _executed = require("../executed/executed");
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
const shortHash = hash => hash ? hash.substring(0, 8) : '';
|
|
12
|
-
const getPendingMigrations = ({
|
|
13
|
-
plannedMigrations,
|
|
14
|
-
executedMigrations,
|
|
15
|
-
printPendingFileNames = false,
|
|
16
|
-
force
|
|
17
|
-
}) => {
|
|
18
|
-
const pendingMigrations = plannedMigrations.filter(m => {
|
|
19
|
-
const executed = executedMigrations.find(e => e?.fileName === m.fileName);
|
|
20
|
-
const skippedFileHashes = m?.skippedFileHashes;
|
|
21
|
-
const isSkippedAndPreviouslyExecuted = skippedFileHashes?.includes(executed?.fileHash);
|
|
22
|
-
/*
|
|
23
|
-
- always execute migrations that have never been executed
|
|
24
|
-
- re-execute migrations only when all are true:
|
|
25
|
-
- fileHash has changed
|
|
26
|
-
- force flag is active
|
|
27
|
-
- the migration was not previously executed and skipped (see .skip feature for patching migration bugs)
|
|
28
|
-
*/
|
|
29
|
-
return executed === undefined || executed?.fileHash !== m?.fileHash && force && !isSkippedAndPreviouslyExecuted;
|
|
30
|
-
});
|
|
31
|
-
if (printPendingFileNames) {
|
|
32
|
-
pendingMigrations.forEach(m => {
|
|
33
|
-
console.log(_chalk.default.gray(` ${m.fileName} ${shortHash(m?.fileHash)}`));
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return pendingMigrations.map(m => ({
|
|
37
|
-
fileName: m.fileName,
|
|
38
|
-
migrator: m.migrator
|
|
39
|
-
}));
|
|
40
|
-
};
|
|
41
|
-
exports.getPendingMigrations = getPendingMigrations;
|
|
42
|
-
const getPendingMigrators = async ({
|
|
43
|
-
config
|
|
44
|
-
}) => {
|
|
45
|
-
const {
|
|
46
|
-
directory,
|
|
47
|
-
database,
|
|
48
|
-
pgpHelpers,
|
|
49
|
-
entity,
|
|
50
|
-
version,
|
|
51
|
-
plan,
|
|
52
|
-
printPendingFileNames,
|
|
53
|
-
force
|
|
54
|
-
} = config;
|
|
55
|
-
const {
|
|
56
|
-
plannedMigrations,
|
|
57
|
-
nextVersion
|
|
58
|
-
} = plan || (await (0, _plan.getPlannedMigrations)({
|
|
59
|
-
config
|
|
60
|
-
}));
|
|
61
|
-
const executedMigrations = database ? await (0, _executed.getExecutedMigrationsFromVersion)(database, pgpHelpers, entity, directory, version, nextVersion, plannedMigrations) : [];
|
|
62
|
-
const pendingMigrators = getPendingMigrations({
|
|
63
|
-
plannedMigrations,
|
|
64
|
-
executedMigrations,
|
|
65
|
-
printPendingFileNames,
|
|
66
|
-
force
|
|
67
|
-
});
|
|
68
|
-
return {
|
|
69
|
-
pendingMigrators,
|
|
70
|
-
nextVersion: plannedMigrations.length < executedMigrations.length ? version : nextVersion
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
exports.getPendingMigrators = getPendingMigrators;
|
package/dist/pipe/pipe.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.pipe = void 0;
|
|
7
|
-
const errorMessage = (fileName, message, stack) => `Unhandled exception in file ${fileName}: ${message}
|
|
8
|
-
|
|
9
|
-
${stack}
|
|
10
|
-
`;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Executes the pending migrators on a payload, returns modified payload
|
|
14
|
-
*
|
|
15
|
-
* Loosely based on:
|
|
16
|
-
* - Eric Elliott's pipe implementation https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d
|
|
17
|
-
* - lodash flow https://lodash.com/docs/4.17.15#flow
|
|
18
|
-
*
|
|
19
|
-
* Implementation includes exception handling to extend errors with the filename
|
|
20
|
-
* until module-with-string fixes its stack trace handling (see OW-8879 and
|
|
21
|
-
* https://github.com/exuanbo/module-from-string/issues/18)
|
|
22
|
-
*
|
|
23
|
-
* @param {Array<Object>} migrations
|
|
24
|
-
* @param {Object} payload
|
|
25
|
-
* @returns {Object} migratedPayload
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
const pipe = (migrations, payload) => migrations.reduce((v, m) => {
|
|
29
|
-
if (typeof m?.migrator !== 'function') {
|
|
30
|
-
throw new TypeError('Expected a function');
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
return m.migrator(v);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
throw new Error(errorMessage(m?.fileName, error.message, error.stack));
|
|
36
|
-
}
|
|
37
|
-
}, payload);
|
|
38
|
-
exports.pipe = pipe;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.setCachedPlannedVersion = exports.setCachedMigratorFunctions = exports.getCachedPlannedVersion = exports.getCachedMigratorFunctions = exports.cacheHasAllMigrationFunctions = void 0;
|
|
7
|
-
let cachedPlannedVersion = null;
|
|
8
|
-
let cachedMigratorFunctions = [];
|
|
9
|
-
const getCachedPlannedVersion = () => cachedPlannedVersion;
|
|
10
|
-
exports.getCachedPlannedVersion = getCachedPlannedVersion;
|
|
11
|
-
const setCachedPlannedVersion = version => {
|
|
12
|
-
cachedPlannedVersion = version;
|
|
13
|
-
};
|
|
14
|
-
exports.setCachedPlannedVersion = setCachedPlannedVersion;
|
|
15
|
-
const getCachedMigratorFunctions = () => cachedMigratorFunctions;
|
|
16
|
-
exports.getCachedMigratorFunctions = getCachedMigratorFunctions;
|
|
17
|
-
const setCachedMigratorFunctions = scripts => {
|
|
18
|
-
cachedMigratorFunctions = scripts;
|
|
19
|
-
};
|
|
20
|
-
exports.setCachedMigratorFunctions = setCachedMigratorFunctions;
|
|
21
|
-
const cacheHasAllMigrationFunctions = ({
|
|
22
|
-
sortedMigrationEntries,
|
|
23
|
-
cachedMigrationEntries
|
|
24
|
-
}) => sortedMigrationEntries.every(migration => cachedMigrationEntries.some(cached => cached.fileHash === migration.fileHash && typeof cached.migrator === 'function'));
|
|
25
|
-
exports.cacheHasAllMigrationFunctions = cacheHasAllMigrationFunctions;
|
package/dist/plan/plan.js
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.writePlan = exports.sortPlanEntries = exports.readPlan = exports.parsePlan = exports.loadMigrationFunctionsFromStrings = exports.loadMigrationFunctions = exports.getPlannedMigrations = exports.getNextSequenceString = exports.generatePlanEntries = exports.formatJSON = void 0;
|
|
7
|
-
var _path = _interopRequireDefault(require("path"));
|
|
8
|
-
var _lodash = require("lodash");
|
|
9
|
-
var _fs = require("fs");
|
|
10
|
-
var _moduleFromString = require("module-from-string");
|
|
11
|
-
var _hash = require("../hash/hash");
|
|
12
|
-
var _glob = require("../glob/glob");
|
|
13
|
-
var _validator = require("./validator");
|
|
14
|
-
var _database = require("../database/database");
|
|
15
|
-
var _cachedPlannedVersion = require("./cached-planned-version");
|
|
16
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
|
-
/**
|
|
18
|
-
* Format JSON with 2-space indentation
|
|
19
|
-
*
|
|
20
|
-
* @param {String} json
|
|
21
|
-
* @returns {String} formatted json
|
|
22
|
-
*/
|
|
23
|
-
const formatJSON = json => {
|
|
24
|
-
try {
|
|
25
|
-
return JSON.stringify(JSON.parse(json), null, 2);
|
|
26
|
-
} catch (e) {
|
|
27
|
-
console.warn('Unable to parse JSON');
|
|
28
|
-
throw new Error(e);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
//https://stackoverflow.com/a/31102605/942635
|
|
33
|
-
exports.formatJSON = formatJSON;
|
|
34
|
-
const sortEntry = entry => Object.keys(entry).sort().reduce((obj, key) => {
|
|
35
|
-
obj[key] = entry[key];
|
|
36
|
-
return obj;
|
|
37
|
-
}, {});
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Sorts the plan entries by sequence string
|
|
41
|
-
*
|
|
42
|
-
* @param {Array<Object>} plannedMigrations
|
|
43
|
-
* returns Array<Object>
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
const maxSequence = 1000000;
|
|
47
|
-
const sortPlanEntries = plannedMigrations => plannedMigrations
|
|
48
|
-
//dotted sequence sort https://stackoverflow.com/a/40201629/942635
|
|
49
|
-
.sort((a, b) => a.sequence.replace(/\d+/g, n => +n + maxSequence).localeCompare(b.sequence.replace(/\d+/g, n => +n + maxSequence))).map(p => sortEntry(p));
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get next automatic sequence string
|
|
53
|
-
*
|
|
54
|
-
* @param {String} sortedPlannedMigrations
|
|
55
|
-
* returns String next sequence
|
|
56
|
-
*/
|
|
57
|
-
exports.sortPlanEntries = sortPlanEntries;
|
|
58
|
-
const getNextSequenceString = sortedPlannedMigrations => {
|
|
59
|
-
const lastMigration = sortedPlannedMigrations[sortedPlannedMigrations.length - 1];
|
|
60
|
-
if (!lastMigration) {
|
|
61
|
-
return '1';
|
|
62
|
-
}
|
|
63
|
-
const currentLastSequenceNumber = lastMigration.sequence;
|
|
64
|
-
return `${parseInt(`${currentLastSequenceNumber}`, 10) + 1}`;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Read the plan.json file
|
|
69
|
-
*
|
|
70
|
-
* @param {String} directory path to migrations directory
|
|
71
|
-
* returns Promise<String> plan.json string
|
|
72
|
-
*/
|
|
73
|
-
exports.getNextSequenceString = getNextSequenceString;
|
|
74
|
-
const readPlan = async directory => {
|
|
75
|
-
let plan;
|
|
76
|
-
try {
|
|
77
|
-
plan = await _fs.promises.readFile(`${directory}/plan.json`, {
|
|
78
|
-
encoding: 'utf8'
|
|
79
|
-
});
|
|
80
|
-
return formatJSON(plan);
|
|
81
|
-
} catch {
|
|
82
|
-
throw new Error('Invalid JSON for migrator plan.js (unable to parse file)');
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Parse the plan.json string into a sorted object
|
|
88
|
-
*
|
|
89
|
-
* @param {String} plan plan.json string
|
|
90
|
-
* returns Array<Object> migration entries from plan.json
|
|
91
|
-
*/
|
|
92
|
-
exports.readPlan = readPlan;
|
|
93
|
-
const parsePlan = plan => {
|
|
94
|
-
let plannedMigrations;
|
|
95
|
-
try {
|
|
96
|
-
plannedMigrations = JSON.parse(plan);
|
|
97
|
-
} catch {
|
|
98
|
-
throw new Error('Invalid JSON for migrator plan.js (unable to parse file)');
|
|
99
|
-
}
|
|
100
|
-
if (!(0, _validator.validatePlan)(plannedMigrations)) {
|
|
101
|
-
throw new Error('Invalid JSON for migrator plan.js (does not match schema)');
|
|
102
|
-
}
|
|
103
|
-
return sortPlanEntries(plannedMigrations);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Write the plan.json file
|
|
108
|
-
*
|
|
109
|
-
* @param {String} directory path to migrations directory
|
|
110
|
-
* @param {String} content string to be written to file
|
|
111
|
-
* returns Promise<>
|
|
112
|
-
*/
|
|
113
|
-
exports.parsePlan = parsePlan;
|
|
114
|
-
const writePlan = async (directory, content) => {
|
|
115
|
-
//todo this is not being written in the correct order
|
|
116
|
-
await _fs.promises.writeFile(_path.default.resolve(`${directory}/plan.json`), content, {
|
|
117
|
-
encoding: 'utf8'
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Generate plan entries
|
|
123
|
-
*
|
|
124
|
-
* @param {Array<Object>} sortedMigrationEntries
|
|
125
|
-
* returns Array<Object>
|
|
126
|
-
*/
|
|
127
|
-
exports.writePlan = writePlan;
|
|
128
|
-
const generatePlanEntries = sortedMigrationEntries => sortedMigrationEntries.map(m => ({
|
|
129
|
-
fileHash: m.fileHash,
|
|
130
|
-
fileName: m.fileName,
|
|
131
|
-
sequence: m.sequence
|
|
132
|
-
}));
|
|
133
|
-
exports.generatePlanEntries = generatePlanEntries;
|
|
134
|
-
const loadMigrationFunctionsFromStrings = async (sortedMigrationEntries, importModule) => Promise.all(sortedMigrationEntries.map(async m => {
|
|
135
|
-
const {
|
|
136
|
-
script
|
|
137
|
-
} = m;
|
|
138
|
-
const migrator = script && importModule ? (await (0, _moduleFromString.importFromString)(script))?.default : null;
|
|
139
|
-
return {
|
|
140
|
-
fileHash: m.fileHash,
|
|
141
|
-
migrator
|
|
142
|
-
};
|
|
143
|
-
}));
|
|
144
|
-
exports.loadMigrationFunctionsFromStrings = loadMigrationFunctionsFromStrings;
|
|
145
|
-
const loadMigrationFunctions = async (sortedMigrationEntries, nextVersion, importModule = true) => {
|
|
146
|
-
const cachedPlannedVersion = (0, _cachedPlannedVersion.getCachedPlannedVersion)();
|
|
147
|
-
const cachedMigratorFunctions = (0, _cachedPlannedVersion.getCachedMigratorFunctions)();
|
|
148
|
-
const cacheValid = cachedPlannedVersion === nextVersion && (0, _cachedPlannedVersion.cacheHasAllMigrationFunctions)({
|
|
149
|
-
sortedMigrationEntries,
|
|
150
|
-
cachedMigrationEntries: cachedMigratorFunctions
|
|
151
|
-
});
|
|
152
|
-
/*
|
|
153
|
-
OW-15584 the module-from-string package seems to have a memory leak bug
|
|
154
|
-
where calling importFromString multiple times consumes memory that
|
|
155
|
-
doesn't get released or garbage collected. We cache the imported strings
|
|
156
|
-
as a workaround (until plan.json version changes)
|
|
157
|
-
*/
|
|
158
|
-
const migrationFunctions = cacheValid ? cachedMigratorFunctions : await loadMigrationFunctionsFromStrings(sortedMigrationEntries, importModule);
|
|
159
|
-
if (!cacheValid) {
|
|
160
|
-
(0, _cachedPlannedVersion.setCachedMigratorFunctions)(migrationFunctions);
|
|
161
|
-
(0, _cachedPlannedVersion.setCachedPlannedVersion)(nextVersion);
|
|
162
|
-
}
|
|
163
|
-
return (0, _lodash.merge)(sortedMigrationEntries, migrationFunctions);
|
|
164
|
-
};
|
|
165
|
-
exports.loadMigrationFunctions = loadMigrationFunctions;
|
|
166
|
-
const getPlannedMigrations = async ({
|
|
167
|
-
config
|
|
168
|
-
}) => {
|
|
169
|
-
const {
|
|
170
|
-
directory,
|
|
171
|
-
database,
|
|
172
|
-
entity,
|
|
173
|
-
force,
|
|
174
|
-
pgpHelpers,
|
|
175
|
-
dry,
|
|
176
|
-
importModule = true
|
|
177
|
-
} = config;
|
|
178
|
-
if (database && !dry) {
|
|
179
|
-
/*
|
|
180
|
-
OW-9043 and OW-9238: The tool tries to self-create its own meta tables
|
|
181
|
-
to simplify setup. That's useful for devs running on local machines,
|
|
182
|
-
and when running via CLI jobs. But for environments without CREATE
|
|
183
|
-
privileges, we want this don't want to execute CREATE TABLE IF NOT EXISTS
|
|
184
|
-
because it throws errors.
|
|
185
|
-
*/
|
|
186
|
-
const tablesExist = await (0, _database.migrationTablesExist)(database, entity);
|
|
187
|
-
if (!tablesExist) {
|
|
188
|
-
await (0, _database.createMigrationsTables)(database, entity);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const rawPlan = await readPlan(directory);
|
|
192
|
-
const parsedPlan = parsePlan(rawPlan);
|
|
193
|
-
const plannedMigrations = parsedPlan.map(m => ({
|
|
194
|
-
fileHashFromPlan: m.fileHash,
|
|
195
|
-
fileName: m.fileName,
|
|
196
|
-
sequence: m.sequence
|
|
197
|
-
}));
|
|
198
|
-
const filePaths = await (0, _glob.getMigrationFilePaths)(directory);
|
|
199
|
-
const skippedFilePaths = await (0, _glob.getSkippedFilePaths)(directory);
|
|
200
|
-
const plannedMigrationsWithFileEntries = await Promise.all(plannedMigrations.map(async m => {
|
|
201
|
-
const {
|
|
202
|
-
fileName
|
|
203
|
-
} = m;
|
|
204
|
-
const isValidFileName = (0, _validator.validateFileName)(fileName);
|
|
205
|
-
const filePath = filePaths.find(f => _path.default.parse(f).base === m.fileName);
|
|
206
|
-
const script = filePath ? await _fs.promises.readFile(filePath, {
|
|
207
|
-
encoding: 'utf8'
|
|
208
|
-
}) : null;
|
|
209
|
-
const fileHash = script ? (0, _hash.hash)(script) : null;
|
|
210
|
-
|
|
211
|
-
//skipped files
|
|
212
|
-
const skipMatch = '(.skip|.skip\\d+).js$';
|
|
213
|
-
const nameMatch = _path.default.parse(m.fileName).name;
|
|
214
|
-
const regex = new RegExp(`${nameMatch}${skipMatch}`, 'g');
|
|
215
|
-
const matchingSkippedFilePaths = skippedFilePaths.filter(f => {
|
|
216
|
-
const name = _path.default.parse(f).base;
|
|
217
|
-
return name.match(regex);
|
|
218
|
-
});
|
|
219
|
-
const skippedFileHashes = await Promise.all(matchingSkippedFilePaths.map(async skippedFilePath => {
|
|
220
|
-
const skippedScript = await _fs.promises.readFile(skippedFilePath, {
|
|
221
|
-
encoding: 'utf8'
|
|
222
|
-
});
|
|
223
|
-
return skippedScript ? (0, _hash.hash)(skippedScript) : null;
|
|
224
|
-
}));
|
|
225
|
-
return {
|
|
226
|
-
...m,
|
|
227
|
-
filePath,
|
|
228
|
-
isValidFileName,
|
|
229
|
-
script,
|
|
230
|
-
fileHash,
|
|
231
|
-
skippedFileHashes
|
|
232
|
-
};
|
|
233
|
-
}));
|
|
234
|
-
const historicalMigrations = database ? await (0, _database.getMigrationRecords)(database, entity) : [];
|
|
235
|
-
const plannedMigrationsWithHistory = plannedMigrationsWithFileEntries.map(m => {
|
|
236
|
-
const historicalMigration = historicalMigrations.find(h => h.fileName === m.fileName);
|
|
237
|
-
return {
|
|
238
|
-
...m,
|
|
239
|
-
fileHashFromHistory: historicalMigration?.fileHash || null,
|
|
240
|
-
sequenceFromHistory: historicalMigration?.sequence || null
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
(0, _validator.throwIfFilesNotFound)(plannedMigrationsWithHistory, importModule);
|
|
244
|
-
(0, _validator.throwIfFileNamesInvalid)(plannedMigrationsWithHistory);
|
|
245
|
-
(0, _validator.throwIfFileNamesNotUnique)(plannedMigrationsWithHistory);
|
|
246
|
-
(0, _validator.throwIfSequenceNotUnique)(plannedMigrationsWithHistory);
|
|
247
|
-
if (!force) {
|
|
248
|
-
(0, _validator.warnIfFilesHaveBeenRemovedFromPlan)(plannedMigrationsWithHistory, historicalMigrations);
|
|
249
|
-
(0, _validator.throwIfFilesHaveChanged)(plannedMigrationsWithHistory);
|
|
250
|
-
(0, _validator.throwIfSequenceHasChanged)(plannedMigrationsWithHistory);
|
|
251
|
-
}
|
|
252
|
-
(0, _validator.throwIfSequenceHasIntegerGaps)(plannedMigrationsWithHistory);
|
|
253
|
-
const validatedPlannedMigrations = generatePlanEntries(plannedMigrationsWithHistory);
|
|
254
|
-
const validatedPlan = JSON.stringify(validatedPlannedMigrations, null, 2);
|
|
255
|
-
const nextVersion = (0, _hash.hash)(validatedPlan);
|
|
256
|
-
const planHasChanged = validatedPlan !== rawPlan;
|
|
257
|
-
if (!dry && planHasChanged) {
|
|
258
|
-
await writePlan(directory, validatedPlan);
|
|
259
|
-
}
|
|
260
|
-
if (database && !dry) {
|
|
261
|
-
await (0, _database.replaceMigrationsRecords)(database, pgpHelpers, entity, validatedPlannedMigrations);
|
|
262
|
-
const versionRecord = [{
|
|
263
|
-
version: nextVersion,
|
|
264
|
-
plan: validatedPlan
|
|
265
|
-
}];
|
|
266
|
-
await (0, _database.insertVersions)(database, pgpHelpers, entity, versionRecord);
|
|
267
|
-
}
|
|
268
|
-
const plannedMigrationsWithFunctions = await loadMigrationFunctions(plannedMigrationsWithHistory, nextVersion, importModule);
|
|
269
|
-
return {
|
|
270
|
-
plannedMigrations: plannedMigrationsWithFunctions,
|
|
271
|
-
nextVersion
|
|
272
|
-
};
|
|
273
|
-
};
|
|
274
|
-
exports.getPlannedMigrations = getPlannedMigrations;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"type": "array",
|
|
3
|
-
"items": {
|
|
4
|
-
"type": "object",
|
|
5
|
-
"properties": {
|
|
6
|
-
"fileHash": {
|
|
7
|
-
"type": ["string"]
|
|
8
|
-
},
|
|
9
|
-
"fileName": {
|
|
10
|
-
"type": "string"
|
|
11
|
-
},
|
|
12
|
-
"sequence": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"pattern": "^[1-9][0-9]*(.[1-9][0-9]*)*$"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"required": ["fileHash", "fileName", "sequence"],
|
|
18
|
-
"additionalProperties": false
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getPlannedVersion = void 0;
|
|
7
|
-
var _hash = require("../hash/hash");
|
|
8
|
-
var _plan = require("./plan");
|
|
9
|
-
var _cachedPlannedVersion = require("./cached-planned-version");
|
|
10
|
-
const getPlannedVersion = async ({
|
|
11
|
-
config
|
|
12
|
-
}) => {
|
|
13
|
-
const {
|
|
14
|
-
directory
|
|
15
|
-
} = config;
|
|
16
|
-
const currentPlan = await (0, _plan.readPlan)(directory);
|
|
17
|
-
const plannedVersion = (0, _hash.hash)(currentPlan);
|
|
18
|
-
const cachedPlannedVersion = (0, _cachedPlannedVersion.getCachedPlannedVersion)();
|
|
19
|
-
return cachedPlannedVersion === plannedVersion ? cachedPlannedVersion : (await (0, _plan.getPlannedMigrations)({
|
|
20
|
-
config: {
|
|
21
|
-
...config,
|
|
22
|
-
importModule: false
|
|
23
|
-
}
|
|
24
|
-
})).nextVersion;
|
|
25
|
-
};
|
|
26
|
-
exports.getPlannedVersion = getPlannedVersion;
|