@voximplant/voxengine-ci
Version:
Manage Voximplant Platform `applications`, `rules` and `scenarios` from your own environment
228 lines (227 loc) • 12.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VoxScenarioService = void 0;
const node_util_1 = require("node:util");
const node_crypto_1 = require("node:crypto");
const node_child_process_1 = require("node:child_process");
const vox_scenario_entity_1 = require("../entities/vox-scenario.entity");
const log_message_generator_1 = require("../../utils/log-message-generator");
const asyncExec = (0, node_util_1.promisify)(node_child_process_1.exec);
class VoxScenarioService {
constructor(platformRepository, persistentRepository) {
this.platformRepository = platformRepository;
this.persistentRepository = persistentRepository;
this.lmg = log_message_generator_1.LogMessageGeneratorFactory.getInstance();
this.init = () => {
console.info(this.lmg.generate('INFO__INIT_SUCCESS', this.constructor.name));
};
this.generateHash = (data) => (0, node_crypto_1.createHash)('sha256').update(data).digest('hex');
this.cleanup = async () => {
await this.persistentRepository.unlink();
await this.persistentRepository.link();
await this.persistentRepository.unlinkMetadata();
await this.persistentRepository.linkMetadata();
};
this.cleanupSrc = async () => {
await this.persistentRepository.unlinkSrc();
await this.persistentRepository.linkSrc();
};
this.cleanupDist = async () => {
await this.persistentRepository.unlinkDist();
await this.persistentRepository.linkDist();
};
this.checkScenariosAlreadyExists = async () => {
try {
const scenarios = await this.persistentRepository.readStorage();
const scenariosMetadata = await this.persistentRepository.readMetadataStorage();
return !!scenarios.length && !!scenariosMetadata.length;
}
catch (error) {
console.error(this.lmg.generate('ERR__CHECK_SCENARIO_ALREADY_EXISTS_FAILED', this.constructor.name));
console.error(error);
}
};
this.checkScenariosNames = (rawScenarios) => {
if (!rawScenarios || !rawScenarios.length)
return;
const scenariosNamesGrouped = rawScenarios.reduce((acc, scenario) => {
const { scenarioName } = scenario;
const scenarioNameLowCase = scenarioName.toLowerCase();
acc?.[scenarioNameLowCase]
? acc[scenarioNameLowCase]?.push(scenarioName)
: (acc[scenarioNameLowCase] = [scenarioName]);
return acc;
}, {});
const duplicatedNames = Object.values(scenariosNamesGrouped).filter((names) => names.length > 1);
if (duplicatedNames.length) {
throw new Error(this.lmg.generate('INFO__SAME_SCENARIOS', duplicatedNames.toString()));
}
};
this.downloadScenarios = async () => {
return await this.platformRepository.downloadScenarios();
};
this.downloadScenario = async (scenarioId) => {
try {
const scenario = await this.platformRepository.downloadScenarioById(scenarioId);
if (!scenario) {
throw new Error(this.lmg.generate('ERR__SCENARIO_BY_ID_IS_NOT_FOUND', scenarioId.toString()));
}
return scenario;
}
catch (error) {
console.error(this.lmg.generate('ERR__DOWNLOAD_SCENARIO_FAILED', this.constructor.name));
console.error(error);
}
};
this.saveScenario = async (rawScenario) => {
try {
const voxScenario = new vox_scenario_entity_1.VoxScenario(rawScenario);
await this.persistentRepository.create(voxScenario);
}
catch (error) {
console.error(this.lmg.generate('ERR__SAVE_SCENARIO_FAILED', this.constructor.name));
console.error(error);
}
};
this.addScenariosToRule = async (bindScenarioRequest) => {
try {
return await this.platformRepository.bindScenarios(bindScenarioRequest);
}
catch (error) {
console.error(this.lmg.generate('ERR__ADD_SCENARIOS_TO_RULE_FAILED', this.constructor.name));
console.error(error);
}
};
this.saveScenarioMetadata = async (rawScenario) => {
try {
const hash = this.generateHash(rawScenario.scenarioScript);
const voxScenarioMetadata = new vox_scenario_entity_1.VoxScenarioMetadata(rawScenario, hash);
await this.persistentRepository.createMetadata(voxScenarioMetadata);
}
catch (error) {
console.error(this.lmg.generate('ERR__SAVE_SCENARIO_METADATA_FAILED', this.constructor.name));
console.error(error);
}
};
this.getScenarioInfoFromPlatform = async (scenarioNames) => {
const allScenarios = await this.platformRepository.downloadScenarios();
return scenarioNames
.map((scenarioName) => allScenarios.find((scenario) => scenario.scenarioName === scenarioName))
.filter((scenario) => scenario);
};
this.getScenariosMetadata = async (scenariosNames) => {
const scenariosMetadata = [];
try {
for (const scenarioName of scenariosNames) {
const stringDistScenarioMetadata = await this.persistentRepository.readDistMetadata(scenarioName);
const scenario = (JSON.parse(stringDistScenarioMetadata));
scenariosMetadata.push({
scenarioId: scenario.scenarioId,
hash: scenario.hash,
scenarioName: scenario.scenarioName,
});
}
}
catch (error) {
console.error(this.lmg.generate('ERR__GET_SCENARIOS_METADATA_FAILED', this.constructor.name));
}
return scenariosMetadata;
};
/**
* TODO: The `build` method must to return something
*/
this.build = async (scenarios = []) => {
try {
await this.persistentRepository.createOrUpdateTsConfig(scenarios);
const tsConfigPath = this.persistentRepository.getTsConfigPath();
try {
await asyncExec(`npx tsc -p ${tsConfigPath}`);
}
catch (compileError) {
console.error(this.lmg.generate('ERR__SCENARIOS_COMPILATION_FAILED', scenarios.toString()));
throw compileError;
}
console.info(this.lmg.generate('INFO__SCENARIOS_BUILT', scenarios.toString()));
}
catch (error) {
console.error(this.lmg.generate('ERR__BUILD_FAILED', this.constructor.name));
throw error;
}
finally {
await this.persistentRepository.removeTsConfig();
}
};
this.upload = async (scenarios = [], isForce = false) => {
try {
for (const scenarioName of scenarios) {
const distScenario = await this.persistentRepository.readDist(scenarioName);
if (!distScenario && distScenario !== '') {
throw new Error(this.lmg.generate('ERR__SCENARIO_DIST_IS_NOT_FOUND', scenarioName));
}
const distScenarioHash = this.generateHash(distScenario);
let stringDistScenarioMetadata = await this.persistentRepository.readDistMetadata(scenarioName);
const platformScenarioInfo = await this.platformRepository.downloadScenarioByName(scenarioName);
// Brand-new scenario
if (!stringDistScenarioMetadata && !platformScenarioInfo) {
const addScenarioResult = await this.platformRepository.addScenario(scenarioName, distScenario);
if (!addScenarioResult) {
throw new Error(this.lmg.generate('ERR__SCENARIO_IS_NOT_ADDED', scenarioName));
}
const platformScenarioInfoWithoutScript = await this.platformRepository.downloadScenarioByName(scenarioName);
if (!platformScenarioInfoWithoutScript) {
throw new Error(this.lmg.generate('ERR__SCENARIO_BY_NAME_IS_NOT_FOUND_AFTER_ADDING', scenarioName));
}
const voxScenarioMetadata = new vox_scenario_entity_1.VoxScenarioMetadata(platformScenarioInfoWithoutScript, distScenarioHash);
await this.persistentRepository.createOrUpdateMetadata(voxScenarioMetadata);
continue;
}
// No metadata for the existing scenario
if (!stringDistScenarioMetadata && platformScenarioInfo) {
const platformScenarioHash = this.generateHash(platformScenarioInfo.scenarioScript);
const voxScenarioMetadata = new vox_scenario_entity_1.VoxScenarioMetadata(platformScenarioInfo, platformScenarioHash);
await this.persistentRepository.createOrUpdateMetadata(voxScenarioMetadata);
stringDistScenarioMetadata =
await this.persistentRepository.readDistMetadata(scenarioName);
}
// Updating the existing scenario
if (stringDistScenarioMetadata && platformScenarioInfo) {
const rawDistScenarioMetadata = (JSON.parse(stringDistScenarioMetadata));
const metadataScenarioHash = rawDistScenarioMetadata.hash;
if (distScenarioHash === metadataScenarioHash) {
console.info(this.lmg.generate('INFO__SCENARIO_NOT_CHANGED', scenarioName));
continue;
}
console.info(this.lmg.generate('INFO__SCENARIO_CHANGED', scenarioName));
const scenarioId = rawDistScenarioMetadata.scenarioId;
const platformScenarioInfoWithScript = await this.platformRepository.downloadScenarioById(scenarioId);
if (!platformScenarioInfoWithScript) {
throw new Error(this.lmg.generate('ERR__SCENARIO_BY_ID_AND_NAME_IS_NOT_FOUND_AFTER_ADDING', scenarioId.toString(), scenarioName));
}
const platformScenarioScript = platformScenarioInfoWithScript.scenarioScript;
const platformScenarioHash = this.generateHash(platformScenarioScript);
const platformChanges = metadataScenarioHash !== platformScenarioHash;
// If there are platform changes that emit aborting the operation (no '--force' flag has been provided)
if (platformChanges && !isForce) {
throw new Error(this.lmg.generate('ERR__SCENARIO_IS_CHANGED_FROM_THE_PLATFORM', scenarioId.toString(), scenarioName));
}
// Alert about '--force' flag using to prevent the 'platform changes' aborting
if (platformChanges && isForce) {
console.info(this.lmg.generate('INFO__SCENARIO_FORCE_CHANGE', scenarioId.toString(), scenarioName));
}
const updateScenarioResult = await this.platformRepository.updateScenario(scenarioId, scenarioName, distScenario);
if (!updateScenarioResult) {
throw new Error(this.lmg.generate('ERR__SCENARIO_IS_NOT_UPDATED', scenarioName, scenarioId.toString()));
}
const voxScenarioMetadata = new vox_scenario_entity_1.VoxScenarioMetadata(platformScenarioInfoWithScript, distScenarioHash);
await this.persistentRepository.createOrUpdateMetadata(voxScenarioMetadata);
}
}
}
catch (error) {
console.error(this.lmg.generate('ERR__UPLOAD_FAILED', this.constructor.name));
throw error;
}
};
}
}
exports.VoxScenarioService = VoxScenarioService;