UNPKG

@grouparoo/core

Version:
205 lines (204 loc) 9.11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigWriter = void 0; const actionhero_1 = require("actionhero"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const prettier_1 = __importDefault(require("prettier")); const App_1 = require("../models/App"); const GrouparooModel_1 = require("../models/GrouparooModel"); const Destination_1 = require("../models/Destination"); const Group_1 = require("../models/Group"); const Property_1 = require("../models/Property"); const Setting_1 = require("../models/Setting"); const Source_1 = require("../models/Source"); const codeConfig_1 = require("../classes/codeConfig"); const pluginDetails_1 = require("../modules/pluginDetails"); const GrouparooRecord_1 = require("../models/GrouparooRecord"); const runMode_1 = require("./runMode"); /** * Loading * ---------------------------------------- * 1. Loader tells Writer to cache all files it read. * 2. Writer caches files that are writable (i.e. not locked). It ignores locked * files. * 3. Individual loaders ask the writer for a lock key. Writer responds with the * key unless it has a cached object. * * * Writing * ---------------------------------------- * 1. Delete all files in the cache. * 2. Query the database for all writable objects. These are UNLOCKED Apps, * Sources (and their Schedules), Properties, Groups, and Destinations. * 3. Each config object is retrieved from the model, written to file, and then * cached. * */ let CONFIG_FILE_CACHE = []; var ConfigWriter; (function (ConfigWriter) { // ---------------------------------------- | Helpers function generateId(name, separator = "_") { if (!name || name.length === 0) return; const id = name .toLowerCase() // replace bad characters with a space .replace(/[^a-zA-Z0-9\-_ ]/g, " ") // remove spaces from beginning and end .trim() // replace spaces with underscore .replace(/[ ]/g, separator) // replace multiple word separators with an underscore .replace(/[\-_ ][\-_ ]+/g, separator); if (id.length === 0) throw new Error("Could not generate ID from name."); return id; } ConfigWriter.generateId = generateId; function generateFilePath(object, prefix) { var _a; const id = Array.isArray(object) ? (_a = object[0]) === null || _a === void 0 ? void 0 : _a.id : object === null || object === void 0 ? void 0 : object.id; if (!id) return; let filePath = `${id}.json`; if (prefix) filePath = `${prefix}/${filePath}`; return filePath; } ConfigWriter.generateFilePath = generateFilePath; // ---------------------------------------- | Controllers async function run() { // If we're not in config mode, do nothing. if ((0, runMode_1.getGrouparooRunMode)() !== "cli:config") return; // Any models we see before starting would be from existing code config // files. if (!actionhero_1.api.process.started) return; // Get the config objects before deleting any of the current objects. Then, // if we run into an error, we leave what we had before and don't rewrite // the config files until the objects are fixed through the UI. const configObjects = await getConfigObjects(); await deleteFiles(); await writeFiles(configObjects); return configObjects; } ConfigWriter.run = run; async function getConfigObjects() { let objects = []; const queryParams = { where: { locked: null } }; const queries = { models: await GrouparooModel_1.GrouparooModel.findAll(queryParams), apps: await App_1.App.findAll(queryParams), sources: await Source_1.Source.findAll(queryParams), properties: await Property_1.Property.findAll(queryParams), groups: await Group_1.Group.findAll(queryParams), destinations: await Destination_1.Destination.findAll(queryParams), records: await GrouparooRecord_1.GrouparooRecord.findAll({ include: [GrouparooModel_1.GrouparooModel] }), }; const clusterNameSetting = await Setting_1.Setting.findOne({ where: { pluginName: "core", key: "cluster-name" }, }); if (clusterNameSetting.value !== clusterNameSetting.defaultValue) { queries["settings"] = [clusterNameSetting]; } for (let [type, instances] of Object.entries(queries)) { if (!instances) continue; for (let instance of instances) { const object = await instance.getConfigObject(); // Don't process arrays that have objects missing id values. if (Array.isArray(object) && object.filter((o) => !o.id).length > 0) { continue; } // Don't process objects that have missing id values. if (!Array.isArray(object) && !(object === null || object === void 0 ? void 0 : object.id)) continue; const filePath = instance instanceof GrouparooRecord_1.GrouparooRecord ? `development/${(await instance.$get("model")).getConfigId()}/records.json` : generateFilePath(object, type); const index = objects.findIndex((obj) => obj.filePath === filePath); if (index >= 0) { objects[index].object = [objects[index].object, object].flat(); } else { objects.push({ filePath, object }); } } } return objects; } ConfigWriter.getConfigObjects = getConfigObjects; // ---------------------------------------- | Config File Cache function getConfigFileCache() { return CONFIG_FILE_CACHE; } ConfigWriter.getConfigFileCache = getConfigFileCache; function resetConfigFileCache() { CONFIG_FILE_CACHE = []; } ConfigWriter.resetConfigFileCache = resetConfigFileCache; function getLockKey(configObject) { if ((0, runMode_1.getGrouparooRunMode)() !== "cli:config") { return (0, codeConfig_1.getCodeConfigLockKey)(); } if (isLockable(configObject)) { return "config:writer"; } // If we are in config mode and the file is not lockable (it is JSON file), // we return null. A null value is equivalent to the object being unlocked. return null; } ConfigWriter.getLockKey = getLockKey; function fileIsLockable(absFilePath) { // Otherwise, it is lockable if it is a JS file. const ext = path_1.default.extname(absFilePath); return ext !== ".json"; } function isLockable(object) { const isMatch = (o) => o.id === object.id && o.class === object.class; const cachedFileObj = CONFIG_FILE_CACHE.find((cache) => cache.objects.filter(isMatch).length > 0); // If there is no cached file, we assume the file is locked. This is because // the Writer does not cache locked files. if (!cachedFileObj) return true; // Otherwise, check the file itself. return fileIsLockable(cachedFileObj.absFilePath); } async function cacheConfigFile(cacheObj) { if (fileIsLockable(cacheObj.absFilePath)) return null; CONFIG_FILE_CACHE.push(cacheObj); } ConfigWriter.cacheConfigFile = cacheConfigFile; // ---------------------------------------- | File Writers async function deleteFiles() { for (let { absFilePath } of CONFIG_FILE_CACHE) { if (fs_1.default.existsSync(absFilePath)) fs_1.default.unlinkSync(absFilePath); } resetConfigFileCache(); } async function writeFile({ filePath, object }) { const configDir = await (0, pluginDetails_1.getConfigDir)(true); const configFilePath = path_1.default.join(configDir, filePath); const dir = path_1.default.dirname(configFilePath); if (!fs_1.default.existsSync(dir)) fs_1.default.mkdirSync(dir, { recursive: true }); const stringifyFilter = (_, v) => v === null ? undefined : v; const content = JSON.stringify(object, stringifyFilter, 2); fs_1.default.writeFileSync(configFilePath, prettier_1.default.format(content, { parser: "json" })); cacheConfigFile({ absFilePath: configFilePath, objects: [object] }); return true; } async function writeFiles(configObjects) { for (let configObject of configObjects) { await writeFile(configObject); } } })(ConfigWriter = exports.ConfigWriter || (exports.ConfigWriter = {}));