@freemework/sql.misc.migration
Version:
Hosting library of the Freemework Project.
317 lines • 15.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.FSqlMigrationSources = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const util_1 = require("util");
const url_1 = require("url");
const common_1 = require("@freemework/common");
const existsAsync = (0, util_1.promisify)(fs.exists);
const mkdirAsync = (0, util_1.promisify)(fs.mkdir);
const readdirAsync = (0, util_1.promisify)(fs.readdir);
const readFileAsync = (0, util_1.promisify)(fs.readFile);
const writeFileAsync = (0, util_1.promisify)(fs.writeFile);
class FSqlMigrationSources {
versionNames;
_versions;
/**
* Load data into memory and represent it as FMigrationSources
* @param executionContext
* @param sourceUri Sources url. Support schemas `file:`, `http+tar+gz:` and `https+tar+gz:`
*/
static load(executionContext, sourceUri, opts) {
switch (sourceUri.protocol) {
case UrlSchemas.FILE: {
const sourceDirectory = (0, url_1.fileURLToPath)(sourceUri);
return FSqlMigrationSources.loadFromFilesystem(executionContext, sourceDirectory, opts);
}
case UrlSchemas.HTTP_TAR_GZ:
throw new common_1.FExceptionInvalidOperation("Not implemented yet");
case UrlSchemas.HTTPS_TAR_GZ:
throw new common_1.FExceptionInvalidOperation("Not implemented yet");
default:
throw new NotSupportedUrlSchemaException(sourceUri);
}
}
static async loadFromFilesystem(executionContext, sourceDirectory, opts) {
if (!await existsAsync(sourceDirectory)) {
throw new FSqlMigrationSources.WrongMigrationDataException(`Migration directory '${sourceDirectory}' is not exist`);
}
const migrationBundles = [];
const listVersions = (await readdirAsync(sourceDirectory, { withFileTypes: true }))
.filter(w => w.isDirectory())
.map(directory => directory.name);
const cancellationToken = common_1.FCancellationExecutionContext.of(executionContext).cancellationToken;
if (listVersions.length > 0) {
for (const version of listVersions) {
if (opts !== undefined) {
// Control version load range, skip out range versions
if (opts.versionFrom !== undefined) {
if (version < opts.versionFrom) {
continue;
}
}
if (opts.versionTo !== undefined) {
if (version > opts.versionTo) {
continue;
}
}
}
cancellationToken.throwIfCancellationRequested();
const versionDirectory = path.join(sourceDirectory, version);
const installDirectory = path.join(versionDirectory, FSqlMigrationSources.Direction.INSTALL);
const rollbackDirectory = path.join(versionDirectory, FSqlMigrationSources.Direction.ROLLBACK);
const installBundleItems = [];
const rollbackBundleItems = [];
if (await existsAsync(installDirectory)) {
const migrationFiles = await readdirAsync(installDirectory);
if (migrationFiles.length > 0) {
for (const migrationFile of migrationFiles) {
cancellationToken.throwIfCancellationRequested();
const scriptFile = path.join(installDirectory, migrationFile);
const scriptContent = await readFileAsync(scriptFile, "utf-8");
const scriptKind = resolveScriptKindByExtension(scriptFile);
installBundleItems.push(new FSqlMigrationSources.Script(migrationFile, scriptKind, scriptFile, scriptContent));
}
}
}
if (await existsAsync(rollbackDirectory)) {
const migrationFiles = await readdirAsync(rollbackDirectory);
if (migrationFiles.length > 0) {
for (const migrationFile of migrationFiles) {
cancellationToken.throwIfCancellationRequested();
const scriptFile = path.join(rollbackDirectory, migrationFile);
const scriptContent = await readFileAsync(scriptFile, "utf-8");
const scriptKind = resolveScriptKindByExtension(scriptFile);
rollbackBundleItems.push(new FSqlMigrationSources.Script(migrationFile, scriptKind, scriptFile, scriptContent));
}
}
}
migrationBundles.push(new FSqlMigrationSources.VersionBundle(version, installBundleItems, rollbackBundleItems));
}
}
return new FSqlMigrationSources(migrationBundles);
}
getVersionBundle(versionName) {
const item = this._versions.get(versionName);
if (item === undefined) {
throw new common_1.FExceptionArgument(`No version bundle with name: ${versionName}`, "versionName");
}
return item;
}
map(callbackfn) {
const mappedBundles = [];
for (const versionName of this.versionNames) {
const bundle = this.getVersionBundle(versionName);
const newBundle = bundle.map((item, opts) => {
return callbackfn(item.content, Object.freeze({
itemName: item.name,
direction: opts.direction,
versionName
}));
});
mappedBundles.push(newBundle);
}
return new FSqlMigrationSources(mappedBundles);
}
async saveToFilesystem(executionContext, destinationDirectory) {
if (!(await existsAsync(destinationDirectory))) {
throw new common_1.FExceptionArgument(`Target directory '${destinationDirectory}' not exist. You must provide empty directory.`, "destinationDirectory");
}
const cancellationToken = common_1.FCancellationExecutionContext.of(executionContext).cancellationToken;
for (const versionName of this.versionNames) {
cancellationToken.throwIfCancellationRequested();
const versionDirectory = path.join(destinationDirectory, versionName);
const installDirectory = path.join(versionDirectory, FSqlMigrationSources.Direction.INSTALL);
const rollbackDirectory = path.join(versionDirectory, FSqlMigrationSources.Direction.ROLLBACK);
await mkdirAsync(versionDirectory);
cancellationToken.throwIfCancellationRequested();
await mkdirAsync(installDirectory);
cancellationToken.throwIfCancellationRequested();
await mkdirAsync(rollbackDirectory);
const versionBundle = this.getVersionBundle(versionName);
for (const installItemName of versionBundle.installScriptNames) {
cancellationToken.throwIfCancellationRequested();
const bundleFile = path.join(installDirectory, installItemName);
const bundleItem = versionBundle.getInstallScript(installItemName);
await writeFileAsync(bundleFile, bundleItem.content, "utf-8");
}
for (const rollbackItemName of versionBundle.rollbackScriptNames) {
cancellationToken.throwIfCancellationRequested();
const bundleFile = path.join(rollbackDirectory, rollbackItemName);
const bundleItem = versionBundle.getRollbackScript(rollbackItemName);
await writeFileAsync(bundleFile, bundleItem.content, "utf-8");
}
}
}
constructor(bundles) {
this._versions = new Map(bundles.map(bundle => ([bundle.versionName, bundle])));
this.versionNames = Object.freeze([...this._versions.keys()].sort());
}
}
exports.FSqlMigrationSources = FSqlMigrationSources;
(function (FSqlMigrationSources) {
class WrongMigrationDataException extends common_1.FExceptionInvalidOperation {
}
FSqlMigrationSources.WrongMigrationDataException = WrongMigrationDataException;
class VersionBundle {
versionName;
installScriptNames;
rollbackScriptNames;
_installScripts;
_rollbackScripts;
constructor(versionName, installItems, rollbackItems) {
this.versionName = versionName;
this._installScripts = new Map(installItems.map(installItem => ([installItem.name, installItem])));
this._rollbackScripts = new Map(rollbackItems.map(rollbackItem => ([rollbackItem.name, rollbackItem])));
this.installScriptNames = Object.freeze([...this._installScripts.keys()].sort());
this.rollbackScriptNames = Object.freeze([...this._rollbackScripts.keys()].sort());
}
getInstallScript(itemName) {
const item = this._installScripts.get(itemName);
if (item === undefined) {
throw new common_1.FExceptionArgument(`No bundle item with name: ${itemName}`, "itemName");
}
return item;
}
getRollbackScript(itemName) {
const script = this._rollbackScripts.get(itemName);
if (script === undefined) {
throw new common_1.FExceptionArgument(`No bundle item with name: ${itemName}`, "itemName");
}
return script;
}
map(callbackFn) {
const installScripts = VersionBundle._map(this.installScriptNames, this._installScripts, (item) => {
return callbackFn(item, { direction: FSqlMigrationSources.Direction.INSTALL });
});
const rollbackScripts = VersionBundle._map(this.rollbackScriptNames, this._rollbackScripts, (item) => {
return callbackFn(item, { direction: FSqlMigrationSources.Direction.ROLLBACK });
});
return new VersionBundle(this.versionName, installScripts, rollbackScripts);
}
static _map(itemNames, itemsMap, callbackfn) {
const mappedItems = [];
for (const itemName of itemNames) {
const item = itemsMap.get(itemName);
const newContent = callbackfn(item);
mappedItems.push(new Script(item.name, item.kind, item.file, newContent));
}
return mappedItems;
}
}
FSqlMigrationSources.VersionBundle = VersionBundle;
class Script {
name;
kind;
file;
content;
constructor(name, kind, file, content) {
this.name = name;
this.kind = kind;
this.file = file;
this.content = content;
}
}
FSqlMigrationSources.Script = Script;
(function (Script) {
let Kind;
(function (Kind) {
Kind["SQL"] = "SQL";
Kind["JAVASCRIPT"] = "JAVASCRIPT";
Kind["UNKNOWN"] = "UNKNOWN";
})(Kind = Script.Kind || (Script.Kind = {}));
(function (Kind) {
function guard(kind) {
const friendlyValue = kind;
switch (friendlyValue) {
case Kind.SQL:
case Kind.JAVASCRIPT:
case Kind.UNKNOWN:
return true;
default:
return guardFalse(friendlyValue);
}
}
Kind.guard = guard;
// tslint:disable-next-line: no-shadowed-variable
function parse(kind) {
const friendlyValue = kind;
if (guard(friendlyValue)) {
return friendlyValue;
}
throw new UnreachableNotSupportedScriptKindException(friendlyValue);
}
Kind.parse = parse;
class UnreachableNotSupportedScriptKindException extends common_1.FExceptionArgument {
constructor(kind) {
super(`Not supported script kind '${JSON.stringify(kind)}'`, "fiatCurrency");
}
}
Kind.UnreachableNotSupportedScriptKindException = UnreachableNotSupportedScriptKindException;
function guardFalse(_never) { return false; }
})(Kind = Script.Kind || (Script.Kind = {}));
})(Script = FSqlMigrationSources.Script || (FSqlMigrationSources.Script = {}));
let Direction;
(function (Direction) {
Direction["INSTALL"] = "install";
Direction["ROLLBACK"] = "rollback";
})(Direction = FSqlMigrationSources.Direction || (FSqlMigrationSources.Direction = {}));
})(FSqlMigrationSources || (exports.FSqlMigrationSources = FSqlMigrationSources = {}));
var UrlSchemas;
(function (UrlSchemas) {
UrlSchemas["FILE"] = "file:";
UrlSchemas["HTTP_TAR_GZ"] = "http+tar+gz:";
UrlSchemas["HTTPS_TAR_GZ"] = "https+tar+gz:";
})(UrlSchemas || (UrlSchemas = {}));
class NotSupportedUrlSchemaException extends common_1.FExceptionInvalidOperation {
constructor(uri) {
super(`Not supported schema: ${uri}`);
}
}
const sqlFilesExtensions = Object.freeze([".sql"]);
const jsFilesExtensions = Object.freeze([".js", ".javascript"]);
function resolveScriptKindByExtension(fileName) {
const ext = path.extname(fileName);
if (sqlFilesExtensions.includes(ext)) {
return FSqlMigrationSources.Script.Kind.SQL;
}
if (jsFilesExtensions.includes(ext)) {
return FSqlMigrationSources.Script.Kind.JAVASCRIPT;
}
return FSqlMigrationSources.Script.Kind.UNKNOWN;
}
//# sourceMappingURL=f_sql_migration_sources.js.map