@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
224 lines • 11.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = loadExtensionsFromDir;
exports.unloadAllExtensions = unloadAllExtensions;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const vm_1 = __importDefault(require("vm"));
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const Contracts_1 = require("../Contracts");
const Configurator_1 = __importDefault(require("./Configurator"));
const Environment_1 = require("./Environment");
const index_1 = require("./ExtensionModules/index");
let logger;
const extensionsToLoad = Configurator_1.default.extensions().include;
const extensionsToIgnore = Configurator_1.default.extensions().exclude;
async function loadExtensionsFromDir(container, extensionsDir) {
logger = new processcube_engine_sdk_1.Logger('extension');
const normalizedPath = path_1.default.normalize(extensionsDir);
const absolutePath = path_1.default.resolve(normalizedPath);
if (!fs_1.default.existsSync(absolutePath)) {
logger.warn(`Failure in extension: Unable to load extensions: Path ${absolutePath} does not exist.`);
return;
}
if (extensionsToLoad != null && extensionsToLoad.length === 0) {
logger.warn(`Engine Setting "extensions.include" is set to an empty Array. No extensions will be loaded.`);
return;
}
if (extensionsToLoad && extensionsToLoad.length > 0) {
logger.info(`The following extensions have been configured with the 'include' list and will be loaded: ${extensionsToLoad.join(', ')}`);
}
if (extensionsToIgnore && extensionsToIgnore.length > 0) {
logger.info(`The following extensions have been configured with the 'exclude' list and will NOT be loaded: ${extensionsToIgnore.join(', ')}`);
}
const extensionPaths = getExtensionPathsFromDir(absolutePath);
if (!extensionPaths || extensionPaths.length === 0) {
logger.info(`No extensions found in ${absolutePath}.`);
return;
}
const engine = createEngineObject(container);
index_1.customServiceTask.initialize(container);
index_1.customHttpRoute.initialize(container);
index_1.eventMiddlewares.initialize(container);
index_1.triggerEvents.initialize(container);
index_1.getEvents.initialize(container);
index_1.userTaskAssignment.initialize(container);
const extensionInfoService = container.get(Contracts_1.IocRegistrationKeys.internal.ExtensionInfoService);
await loadExtensions(extensionPaths, absolutePath, engine, extensionInfoService);
}
async function unloadAllExtensions() {
(0, Environment_1.clearOnReadyCallbacks)();
}
function getExtensionPathsFromDir(absolutePath) {
const absolutePathStats = fs_1.default.statSync(absolutePath);
if (!absolutePathStats.isDirectory()) {
logger.warn(`Unable to load extensions: Path ${absolutePath} is not a directory.`);
return [];
}
const extensionPaths = fs_1.default
.readdirSync(absolutePath, { withFileTypes: true })
.filter((item) => item.isDirectory())
.map((item) => path_1.default.join(absolutePath, item.name));
return extensionPaths;
}
async function loadExtensions(extensionPaths, basePath, engine, extensionInfoService) {
logger.info(`Loading ${extensionPaths.length} extensions from ${basePath}.`);
for (const extensionPath of extensionPaths) {
try {
const extension = await requireExtensionFromPath(extensionPath);
const extensionIsWhitelisted = extensionsToLoad == null || extensionsToLoad.includes(extension.name);
const extensionIsBlacklisted = extensionsToIgnore != null && extensionsToIgnore.includes(extension.name);
if (!extensionIsWhitelisted || extensionIsBlacklisted) {
logger.warn(`Ignoring Extension "${extension.name}" as per include/exclude settings.`);
continue;
}
logger.debug(`Loading extension ${extension.name}...`);
await loadExtension(extension, engine);
extensionInfoService.addLoadedExtension({ name: extension.name, version: extension.version });
logger.info(`Loaded extension ${extension.name} version ${extension.version}.`);
}
catch (error) {
logger.error(`Failure in extension: Error while loading extension from ${extensionPath}.`, {
extensionPath: extensionPath,
err: error,
});
error.category = 'extension';
error.message = `Failure in extension: ${error.message}`;
throw error;
}
}
logger.info(`Loaded ${extensionInfoService.getLoadedExtensions().length} of ${extensionPaths.length} extensions from ${basePath}.`);
}
async function requireExtensionFromPath(extensionPath) {
const packageJsonContent = getPackageJsonContent(extensionPath);
const mainPath = path_1.default.join(extensionPath, packageJsonContent.main);
const exports = await vm_1.default.runInNewContext('require(mainPath)', {
require: require,
mainPath: mainPath,
});
const extension = {
exports: exports,
name: packageJsonContent.name,
version: packageJsonContent.version,
path: mainPath,
};
return extension;
}
function getPackageJsonContent(extensionPath) {
const packageJsonPath = path_1.default.join(extensionPath, 'package.json');
const packageFileContent = fs_1.default.readFileSync(packageJsonPath, 'utf8');
if (!packageFileContent) {
throw new Error(`${packageJsonPath} not found.`);
}
const packageJsonContent = JSON.parse(packageFileContent);
const missingRequiredProperties = [];
if (packageJsonContent?.name == null) {
missingRequiredProperties.push('name');
}
if (packageJsonContent?.main == null) {
missingRequiredProperties.push('main');
}
if (packageJsonContent?.version == null) {
missingRequiredProperties.push('version');
}
if (missingRequiredProperties.length > 0) {
throw new Error(`package.json of extension ${packageJsonPath} is missing the following required properties: ${missingRequiredProperties.join(', ')}.`);
}
return packageJsonContent;
}
async function loadExtension(extension, engine) {
if (!extension.exports?.onLoad) {
logger.warn(`Extension at ${extension.path} exports no onLoad function.`);
}
else {
await extension.exports.onLoad(engine);
}
}
let engineIsReady = false;
function createEngineObject(container) {
const processTokenService = container.get(Contracts_1.IocRegistrationKeys.core.services.ProcessTokenService);
(0, Environment_1.registerOnReadyCallback)(() => {
engineIsReady = true;
});
return {
getConfig: (key) => Configurator_1.default.get(key),
onBeforeResuming: Environment_1.registerOnBeforeResumingCallback,
onReady: Environment_1.registerOnReadyCallback,
registerEventMiddleware: index_1.eventMiddlewares.registerEventMiddleware,
removeEventMiddleware: index_1.eventMiddlewares.removeEventMiddleware,
registerMessageEventMiddleware: index_1.eventMiddlewares.registerMessageEventMiddleware,
registerSignalEventMiddleware: index_1.eventMiddlewares.registerSignalEventMiddleware,
triggerMessageEvent: index_1.triggerEvents.triggerMessageEvent,
triggerMessageStartEvent: index_1.triggerEvents.triggerMessageStartEvent,
triggerSignalEvent: index_1.triggerEvents.triggerSignalEvent,
triggerSignalStartEvent: index_1.triggerEvents.triggerSignalStartEvent,
getMessageEvents: index_1.getEvents.getMessageEvents,
getSignalEvents: index_1.getEvents.getSignalEvents,
registerCustomServiceTask: index_1.customServiceTask.registerCustomServiceTask,
removeCustomServiceTask: index_1.customServiceTask.removeCustomServiceTask,
registerUserTaskAssignmentResolver: index_1.userTaskAssignment.registerUserTaskAssignmentResolver,
registerHttpRoute: index_1.customHttpRoute.registerHttpRoute,
executeRuntimeExpression: processTokenService.executeRuntimeExpression.bind(processTokenService),
get iam() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.IamExtensionAdapter);
},
get applicationInfo() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.ApplicationInfoExtensionAdapter);
},
get correlations() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.CorrelationExtensionAdapter);
},
get cronjobs() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.CronjobExtensionAdapter);
},
get dataObjectInstances() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.DataObjectInstanceExtensionAdapter);
},
get untypedTasks() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.UntypedTaskExtensionAdapter);
},
get events() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.EventExtensionAdapter);
},
get externalTasks() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.ExternalTaskExtensionAdapter);
},
get flowNodeInstances() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.FlowNodeInstanceExtensionAdapter);
},
get manualTasks() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.ManualTaskExtensionAdapter);
},
get notification() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.NotificationExtensionAdapter);
},
get processDefinitions() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.ProcessDefinitionExtensionAdapter);
},
get processInstances() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.ProcessInstanceExtensionAdapter);
},
get processModels() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.ProcessModelExtensionAdapter);
},
get userTasks() {
return getExtensionAdapter(container, Contracts_1.IocRegistrationKeys.extension.adapter.UserTaskExtensionAdapter);
},
};
}
const extensionAdapterCache = {};
function getExtensionAdapter(container, containerKey) {
if (!engineIsReady) {
throw new processcube_engine_sdk_1.BadRequestError('The engine is not ready yet.', 'extension');
}
if (extensionAdapterCache[containerKey]) {
return extensionAdapterCache[containerKey];
}
const extensionAdapter = container.get(containerKey);
extensionAdapterCache[containerKey] = extensionAdapter;
return extensionAdapter;
}
//# sourceMappingURL=ExtensionLoader.js.map