UNPKG

@zowe/imperative

Version:
226 lines 10.1 kB
"use strict"; /* * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v2.0 which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v20.html * * SPDX-License-Identifier: EPL-2.0 * * Copyright Contributors to the Zowe Project. * */ Object.defineProperty(exports, "__esModule", { value: true }); exports.EventUtils = void 0; const ImperativeError_1 = require("../../error/src/ImperativeError"); const path_1 = require("path"); const EventConstants_1 = require("./EventConstants"); const fs = require("fs"); const ConfigUtils_1 = require("../../config/src/ConfigUtils"); const Event_1 = require("./Event"); const ImperativeConfig_1 = require("../../utilities/src/ImperativeConfig"); /** * A collection of helper functions related to event processing, including: * - directory management * - event type determination * - subscription creation and setting callbacks * - event writing */ class EventUtils { /** * Retrieves a list of supported applications from configuration. * * @static * @returns {string[]} List of application names. * @throws If the extenders.json file cannot be created when it does not exist. * @throws If the extenders.json file cannot be read. */ static getListOfApps() { const extendersJson = ConfigUtils_1.ConfigUtils.readExtendersJson(); return ["Zowe", ...Object.keys(extendersJson.profileTypes)]; } /** * Won't throw an error if user entered a valid appName * * @static * @param {string} appName - The name of the application. * @throws {ImperativeError} If the application name is not recognized. */ static validateAppName(appName) { const appList = this.getListOfApps(); if (appList.includes(appName)) return; // Performing `appList.find(app => app.includes(appName))` will allow for "tags" (or suffixes) coming from `getListOfApps()` // However, we do not want this behavior because it will allow partial application names to be used // Hence why we should probably match the application name with the exact profileType in `extenders.json` throw new ImperativeError_1.ImperativeError({ msg: `Application name not found: ${appName}. Please use an application name from the list:\n- ${appList.join("\n- ")}` }); } /** * Determines if the specified event name is associated with a user event. * * @param {string} eventName - The name of the event. * @return {boolean} True if it is a user event, otherwise false. */ static isUserEvent(eventName) { return Object.values(EventConstants_1.ZoweUserEvents).includes(eventName); } /** * Determines if the specified event name is associated with a shared event. * * @param {string} eventName - The name of the event. * @return {boolean} True if it is a shared event, otherwise false. */ static isSharedEvent(eventName) { return Object.values(EventConstants_1.ZoweSharedEvents).includes(eventName); } /** * Retrieve the event contents form disk * * @internal This is not intended for application developers * @param eventFilePath The path to the event file * @returns The object representing the Event * @throws {ImperativeError} If the contents of the event cannot be retrieved. */ static getEventContents(eventFilePath) { try { return JSON.parse(fs.readFileSync(eventFilePath).toString()); } catch (err) { throw new ImperativeError_1.ImperativeError({ msg: `Unable to retrieve event contents: Path: ${eventFilePath}` }); } } /** * Determines the directory path for storing event files based on the event type and application name. * * @param {string} appName - The name of the application. * @return {string} The directory path. * @throws {ImperativeError} If the application name is not recognized. */ static getEventDir(appName) { this.validateAppName(appName); return (0, path_1.join)(".events", appName); } /** * Ensures that the specified directory for storing event files exists, creating it if necessary. * * @param {string} directoryPath - The path to the directory. * @throws {ImperativeError} If we are unable to create the '.events' directory. */ static ensureEventsDirExists(directoryPath) { try { if (!fs.existsSync(directoryPath)) { fs.mkdirSync(directoryPath, { mode: 0o750, recursive: true }); // user read/write/exec, group read/exec } } catch (err) { throw new ImperativeError_1.ImperativeError({ msg: `Unable to create '.events' directory. Path: ${directoryPath}`, causeErrors: err }); } } /** * Ensures that the specified file path for storing event data exists, creating it if necessary. * * @param {string} filePath - The path to the file. * @throws {ImperativeError} If we are unable to create the event file required for event emission. */ static ensureFileExists(filePath) { try { // eslint-disable-next-line @typescript-eslint/no-magic-numbers const fd = fs.openSync(filePath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_RDWR, 0o640); fs.closeSync(fd); } catch (err) { if (err.code !== 'EEXIST') { throw new ImperativeError_1.ImperativeError({ msg: `Unable to create event file. Path: ${filePath}`, causeErrors: err }); } } } /** * Create an event with minimal information * * @internal This is not intended for application developers * @param eventName The name of the event. * @param appName The name of the application. * @returns The created event * @throws {ImperativeError} If the application name is not recognized. * @throws {ImperativeError} If we are unable to create the '.events' directory. * @throws {ImperativeError} If we are unable to create the event file required for event emission. */ static createEvent(eventName, appName) { const zoweDir = ImperativeConfig_1.ImperativeConfig.instance.loadedConfig != null ? ImperativeConfig_1.ImperativeConfig.instance.cliHome : ConfigUtils_1.ConfigUtils.getZoweDir(); const dir = (0, path_1.join)(zoweDir, EventUtils.getEventDir(appName)); this.ensureEventsDirExists(dir); const filePath = (0, path_1.join)(dir, eventName); this.ensureFileExists(filePath); return new Event_1.Event({ eventTime: new Date().toISOString(), eventName: eventName, appName: appName, eventFilePath: filePath, subscriptions: [], }); } /** * Creates and registers a new event subscription for a specific event processor. * * @param {EventProcessor} eeInst - The event processor instance. * @param {string} eventName - The name of the event. * @param {EventTypes} eventType - The type of event. * @return {IEventDisposable} An interface for managing the subscription. * @throws {ImperativeError} If the application name is not recognized. * @throws {ImperativeError} If we are unable to create the '.events' directory. * @throws {ImperativeError} If we are unable to create the event file required for event emission. */ static createSubscription(eeInst, eventName, eventType) { const newEvent = EventUtils.createEvent(eventName, eeInst.appName); newEvent.eventType = eventType; eeInst.subscribedEvents.set(eventName, newEvent); return { close: () => eeInst.unsubscribe(eventName) }; } /** * Sets up a file watcher for a specific event, triggering callbacks when the event file is updated. * * @param {EventProcessor} eeInst - The event processor instance. * @param {string} eventName - The name of the event. * @param {EventCallback[] | EventCallback} callbacks - A single callback or an array of callbacks to execute. * @return {fs.FSWatcher} A file system watcher. * @throws {ImperativeError} If the event to be watched does not have an existing file to watch. * @throws {ImperativeError} Callbacks will fail if the contents of the event cannot be retrieved. */ static setupWatcher(eeInst, eventName, callbacks) { const event = eeInst.subscribedEvents.get(eventName); const watcher = fs.watch(event.eventFilePath, (trigger) => { // Accommodates for the delay between actual file change event and fsWatcher's perception //(Node.JS triggers this notification event 3 times) event.eventTime = EventUtils.getEventContents(event.eventFilePath).eventTime; let shouldProcessEvent = eeInst.eventTimes.get(eventName) !== event.eventTime; eeInst.eventTimes.set(eventName, event.eventTime); // Checks that event was not triggered by the same process shouldProcessEvent && (shouldProcessEvent = process.pid !== event.appProcId); delete event.appProcId; if (shouldProcessEvent) { eeInst.logger.debug(`EventEmitter: Event "${trigger}" emitted: ${eventName}`); if (Array.isArray(callbacks)) { callbacks.forEach(cb => cb()); } else { callbacks(); } } }); event.subscriptions.push(watcher); return watcher; } /** * Writes event data to the corresponding event file in JSON format. * * @param {Event} event - The event object. */ static writeEvent(event) { fs.writeFileSync(event.eventFilePath, JSON.stringify(event.toJson(), null, 2)); } } exports.EventUtils = EventUtils; //# sourceMappingURL=EventUtils.js.map