Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVP: Imperative Event Emitter #2118

Merged
merged 33 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f255eb6
feat(events): implement initial phase - Writers
zFernand0 Jan 23, 2024
b6b056c
forgot to export and to strip internal
zFernand0 Jan 23, 2024
a1f1b71
Add the ability to write custom events
zFernand0 Jan 24, 2024
81be315
ensure that imperative utilities are initialized
zFernand0 Jan 25, 2024
b6503a4
Refactor: changed class names, event names, event format to JSON
zFernand0 Jan 26, 2024
71bdc82
Merge branch 'next' of https://github.com/zowe/zowe-cli into poc-1987
zFernand0 Mar 20, 2024
3072c15
add an idea for singleton approach
zFernand0 Mar 20, 2024
328de2f
MVP
zFernand0 Mar 21, 2024
f0f17ed
fix: prevent multiple watchers for the same event
zFernand0 Mar 27, 2024
4f0fcf7
fix: logger of undefined
zFernand0 Mar 27, 2024
7730aba
fix: reuse the logger
zFernand0 Mar 27, 2024
4ee31c7
fixing ENOENT error upon initial subscription to event
Mar 27, 2024
dca31e4
adding method to unsubscribe from events
Mar 27, 2024
b77a0ee
removing subscription history upon deletion
Mar 27, 2024
f2fe260
fix: allow only one event per registration
zFernand0 Mar 27, 2024
c13a40b
outline for tests
Apr 2, 2024
59095c5
fix: properly mock eventemitter in old unit tests
zFernand0 Apr 6, 2024
4800dac
test: half-way through tests
zFernand0 Apr 8, 2024
5816bbd
test: finalize unit-tests on event emitter
zFernand0 Apr 12, 2024
7feae61
lint: fix lint issues
zFernand0 Apr 12, 2024
aa58853
Merge branch 'next' of https://github.com/zowe/zowe-cli into poc-1987…
zFernand0 Apr 12, 2024
a600acd
fix build by allowing other files in packages/imperative/__tests__/__…
zFernand0 Apr 22, 2024
6402b92
test: moved the integration tests odwn to events instead
zFernand0 Apr 22, 2024
dcd805d
refactor Imperative Event
zFernand0 Apr 26, 2024
95304a3
chore: add teardown stage
zFernand0 May 8, 2024
932c6c6
test: update integration test
zFernand0 May 8, 2024
3b24f11
Merge branch 'next' of https://github.com/zowe/zowe-cli into poc-1987…
zFernand0 May 8, 2024
f3a2e05
Merge branch 'next' of https://github.com/zowe/zowe-cli into poc-1987…
zFernand0 May 13, 2024
bd7e040
fix: address PR comments :yum:
zFernand0 May 13, 2024
44e7a80
tests: fix unit tests and add new ones for teardown and getContents
zFernand0 May 13, 2024
31b0591
tests: forgot to mock fs module
zFernand0 May 13, 2024
5e45fd8
chore: address PR feedback
zFernand0 May 13, 2024
d68e05d
chore: update inteface for EventCallback
zFernand0 May 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.
*
*/

import { ImperativeEventEmitter } from "../../src";

describe("Event Emitter", () => {
const iee = ImperativeEventEmitter;
Fixed Show fixed Hide fixed

beforeEach(() => {
jest.restoreAllMocks();
});

describe("Shared Events", () => {
it("should create an event file upon first subscription if the file does not exist", () => { });

Check warning on line 22 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
it("should trigger subscriptions for all instances watching for onCredentialManagerChanged", () => { });

Check warning on line 23 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
it("should not affect subscriptions from another instance when unsubscribing from events", () => { });

Check warning on line 24 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
});

describe("User Events", () => {
it("should create an event file upon first subscription if the file does not exist", () => { });

Check warning on line 28 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
it("should trigger subscriptions for all instances watching for onVaultChanged", () => { });

Check warning on line 29 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
it("should not affect subscriptions from another instance when unsubscribing from events", () => { });

Check warning on line 30 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
});

describe("Custom Events", () => {
it("should create an event file upon first subscription if the file does not exist", () => { });

Check warning on line 34 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
it("should trigger subscriptions for all instances watching for onMyCustomEvent", () => { });

Check warning on line 35 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
it("should not affect subscriptions from another instance when unsubscribing from events", () => { });

Check warning on line 36 in packages/imperative/__tests__/__integration__/ImperativeEventEmitter.integration.test.ts

View workflow job for this annotation

GitHub Actions / lint

Test has no assertions
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { join } from "path";
jest.mock("../src/syntax/SyntaxValidator");
jest.mock("../src/utils/SharedOptions");
jest.mock("../../utilities/src/ImperativeConfig");
jest.mock("../../events/src/ImperativeEventEmitter");

// Persist the original definitions of process.write
const ORIGINAL_STDOUT_WRITE = process.stdout.write;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { IConfig } from "../src/doc/IConfig";
import { IConfigLayer } from "../src/doc/IConfigLayer";
import { IConfigProfile } from "../src/doc/IConfigProfile";

jest.mock("../../events/src/ImperativeEventEmitter");

const MY_APP = "my_app";

const mergeConfig: IConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Config } from "../src/Config";
import { IConfig } from "../src/doc/IConfig";
import { IConfigSecure } from "../src/doc/IConfigSecure";
import { IConfigVault } from "../src/doc/IConfigVault";
import { ImperativeEventEmitter } from "../../events";

const MY_APP = "my_app";

Expand Down Expand Up @@ -46,6 +47,9 @@ describe("Config secure tests", () => {
});

beforeEach(() => {
jest.spyOn(ImperativeEventEmitter, "initialize").mockImplementation();
Object.defineProperty(ImperativeEventEmitter, "instance", { value: { emitEvent: jest.fn() }});

mockSecureLoad = jest.fn();
mockSecureSave = jest.fn();
mockVault = {
Expand Down
3 changes: 3 additions & 0 deletions packages/imperative/src/config/__tests__/Config.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import { ConfigConstants } from "../src/ConfigConstants";
import * as JSONC from "comment-json";
import { ConfigLayers, ConfigSecure } from "../src/api";


jest.mock("../../events/src/ImperativeEventEmitter");

const MY_APP = "my_app";

describe("Config tests", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*/

jest.mock("../../logger/src/LoggerUtils");
jest.mock("../../events/src/ImperativeEventEmitter");

import { AbstractAuthHandler } from "../../imperative";
import { SessConstants } from "../../rest";
import { ImperativeConfig } from "../../utilities";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { IExtendersJsonOpts } from "../src/doc/IExtenderOpts";
import { ConfigSchema } from "../src/ConfigSchema";
import { Logger } from "../..";

jest.mock("../../events/src/ImperativeEventEmitter");

const testAppNm = "ProfInfoApp";
const testEnvPrefix = testAppNm.toUpperCase();
const profileTypes = ["zosmf", "tso", "base", "dummy"];
Expand Down
4 changes: 4 additions & 0 deletions packages/imperative/src/config/src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import { ConfigUtils } from "./ConfigUtils";
import { IConfigSchemaInfo } from "./doc/IConfigSchema";
import { JsUtils } from "../../utilities/src/JsUtils";
import { IConfigMergeOpts } from "./doc/IConfigMergeOpts";
import { ImperativeEventEmitter } from "../../events";
import { Logger } from "../../logger";

/**
* Enum used by Config class to maintain order of config layers
Expand Down Expand Up @@ -153,6 +155,8 @@ export class Config {
myNewConfig.mVault = opts.vault;
myNewConfig.mSecure = {};

ImperativeEventEmitter.initialize(app, { logger:Logger.getAppLogger() });

// Populate configuration file layers
await myNewConfig.reload(opts);

Expand Down
2 changes: 1 addition & 1 deletion packages/imperative/src/config/src/ConfigBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { IImperativeConfig } from "../../imperative";
import { Config } from "./Config";
import { IConfig } from "./doc/IConfig";
import { IConfigBuilderOpts } from "./doc/IConfigBuilderOpts";
import { CredentialManagerFactory } from "../../security";
import { CredentialManagerFactory } from "../../security/src/CredentialManagerFactory";
import { IConfigConvertResult } from "./doc/IConfigConvertResult";
import { ICommandProfileTypeConfiguration } from "../../cmd";

Expand Down
48 changes: 46 additions & 2 deletions packages/imperative/src/config/src/ConfigUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
*
*/

import { normalize as pathNormalize } from "path";
import { homedir as osHomedir } from "os";
import { normalize as pathNormalize, join as pathJoin } from "path";
import { existsSync as fsExistsSync } from "fs";

import { CredentialManagerFactory } from "../../security";
import { CredentialManagerFactory } from "../../security/src/CredentialManagerFactory";
import { ICommandArguments } from "../../cmd";
import { ImperativeConfig } from "../../utilities";
import { ImperativeError } from "../../error";
import { LoggerManager } from "../../logger/src/LoggerManager";
import { LoggingConfigurer } from "../../imperative/src/LoggingConfigurer";
import { Logger } from "../../logger/src/Logger";
import { EnvironmentalVariableSettings } from "../../imperative/src/env/EnvironmentalVariableSettings";

export class ConfigUtils {
/**
Expand Down Expand Up @@ -114,4 +119,43 @@ export class ConfigUtils {
additionalDetails: details
});
}


// _______________________________________________________________________
/**
* Perform a rudimentary initialization of some Imperative utilities.
* We must do this because VSCode apps do not typically call imperative.init.
* @internal
*/
public static initImpUtils(appName: string) {
// create a rudimentary ImperativeConfig if it has not been initialized
if (ImperativeConfig.instance.loadedConfig == null) {
let homeDir: string = null;
const envVarPrefix = appName.toUpperCase();
const envVarNm = envVarPrefix + EnvironmentalVariableSettings.CLI_HOME_SUFFIX;
if (process.env[envVarNm] === undefined) {
// use OS home directory
homeDir = pathJoin(osHomedir(), "." + appName.toLowerCase());
} else {
// use the available environment variable
homeDir = pathNormalize(process.env[envVarNm]);
}
ImperativeConfig.instance.loadedConfig = {
name: appName,
defaultHome: homeDir,
envVariablePrefix: envVarPrefix
};
ImperativeConfig.instance.rootCommandName = appName;
}

// initialize logging
if (LoggerManager.instance.isLoggerInit === false) {
const loggingConfig = LoggingConfigurer.configureLogger(
ImperativeConfig.instance.cliHome, ImperativeConfig.instance.loadedConfig
);
Logger.initLogger(loggingConfig);
}
return Logger.getImperativeLogger();
}

}
41 changes: 1 addition & 40 deletions packages/imperative/src/config/src/ProfileInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,9 @@ import { ICommandProfileProperty, ICommandArguments } from "../../cmd";
import { IProfileLoaded, IProfileProperty, IProfileSchema } from "../../profiles";

// for imperative operations
import { EnvironmentalVariableSettings } from "../../imperative/src/env/EnvironmentalVariableSettings";
import { LoggingConfigurer } from "../../imperative/src/LoggingConfigurer";
import { CliUtils, ImperativeConfig } from "../../utilities";
import { ImperativeExpect } from "../../expect";
import { Logger, LoggerUtils } from "../../logger";
import { LoggerManager } from "../../logger/src/LoggerManager";
import {
IOptionsForAddConnProps, ISession, Session, SessConstants, ConnectionPropsForSessCfg
} from "../../rest";
Expand Down Expand Up @@ -180,7 +177,7 @@ export class ProfileInfo {
this.mCredentials = new ProfileCredentials(this, profInfoOpts);

// do enough Imperative stuff to let imperative utilities work
this.initImpUtils();
this.mImpLogger = ConfigUtils.initImpUtils(this.mAppName);
}

/**
Expand Down Expand Up @@ -969,42 +966,6 @@ export class ProfileInfo {
}
}

// _______________________________________________________________________
/**
* Perform a rudimentary initialization of some Imperative utilities.
* We must do this because VSCode apps do not typically call imperative.init.
*/
private initImpUtils() {
// create a rudimentary ImperativeConfig if it has not been initialized
if (ImperativeConfig.instance.loadedConfig == null) {
let homeDir: string = null;
const envVarPrefix = this.mAppName.toUpperCase();
const envVarNm = envVarPrefix + EnvironmentalVariableSettings.CLI_HOME_SUFFIX;
if (process.env[envVarNm] === undefined) {
// use OS home directory
homeDir = path.join(os.homedir(), "." + this.mAppName.toLowerCase());
} else {
// use the available environment variable
homeDir = path.normalize(process.env[envVarNm]);
}
ImperativeConfig.instance.loadedConfig = {
name: this.mAppName,
defaultHome: homeDir,
envVariablePrefix: envVarPrefix
};
ImperativeConfig.instance.rootCommandName = this.mAppName;
}

// initialize logging
if (LoggerManager.instance.isLoggerInit === false) {
const loggingConfig = LoggingConfigurer.configureLogger(
ImperativeConfig.instance.cliHome, ImperativeConfig.instance.loadedConfig
);
Logger.initLogger(loggingConfig);
}
this.mImpLogger = Logger.getImperativeLogger();
}

/**
* Load any profile schema objects found on disk and cache them. For team
* config, we check each config layer and load its schema JSON if there is
Expand Down
3 changes: 3 additions & 0 deletions packages/imperative/src/config/src/api/ConfigSecure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { ConfigConstants } from "../ConfigConstants";
import { IConfigProfile } from "../doc/IConfigProfile";
import { CredentialManagerFactory } from "../../../security";
import { ConfigUtils } from "../ConfigUtils";
import { ImperativeEventEmitter } from "../../../events/src/ImperativeEventEmitter";
import { ImperativeUserEvents } from "../../../events/src/ImperativeEventConstants";

/**
* API Class for manipulating config layers.
Expand Down Expand Up @@ -130,6 +132,7 @@ export class ConfigSecure extends ConfigApi {
*/
public async directSave() {
await this.mConfig.mVault.save(ConfigConstants.SECURE_ACCT, JSONC.stringify(this.mConfig.mSecure));
ImperativeEventEmitter.instance.emitEvent(ImperativeUserEvents.ON_VAULT_CHANGED);
}

// _______________________________________________________________________
Expand Down
Loading
Loading