UNPKG

@freemework/sql.misc.migration

Version:

Hosting library of the Freemework Project.

317 lines 15.2 kB
"use strict"; 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