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

feat(extender): Expose and reuse login and logout methods #2606

Merged
merged 22 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d562da3
feat(extender): Expose and reuse login and logout methods
zFernand0 Nov 30, 2023
8f0cfd1
Merge branch 'main' of https://github.com/zowe/vscode-extension-for-z…
zFernand0 Dec 12, 2023
fbbb667
update cli version with forceUpdate enhancement
zFernand0 Dec 12, 2023
60978aa
address typings issues
zFernand0 Dec 13, 2023
31ef735
Merge branch 'main' of https://github.com/zowe/vscode-extension-for-z…
zFernand0 Dec 13, 2023
87461f6
add tests
zFernand0 Dec 13, 2023
52018cf
update profilesCache tests
zFernand0 Dec 13, 2023
e53cf2e
fix lint and sample cli version number
zFernand0 Dec 13, 2023
056e524
forgot to add lint fixes; missing one theia test failure?
zFernand0 Dec 13, 2023
9bc2ddf
Merge branch 'main' into feat-2493
JillieBeanSim Dec 15, 2023
c7beebd
Merge branch 'main' of https://github.com/zowe/vscode-extension-for-z…
zFernand0 Jan 8, 2024
b1d0ae5
Merge branch 'main' of https://github.com/zowe/vscode-extension-for-z…
zFernand0 Jan 10, 2024
7940a5c
update CLI dependency
zFernand0 Jan 11, 2024
90a755a
update changelog to add 2610. thanks @KevinLoesch1
zFernand0 Jan 11, 2024
14c9690
forgot to run prettier. Thanks @traeok
zFernand0 Jan 11, 2024
86dcc7e
Merge branch 'main' of https://github.com/zowe/vscode-extension-for-z…
zFernand0 Jan 11, 2024
0164487
fix #2664 isProfileUsingBasicAuth + fix #2665 APIML dynamic tokens su…
zFernand0 Jan 16, 2024
51d1a7e
Merge branch 'main' of https://github.com/zowe/vscode-extension-for-z…
zFernand0 Jan 16, 2024
9aef3b0
fixed this. in ZoweVsCodeExtension
zFernand0 Jan 16, 2024
de42251
fix #2666 Placeholders
zFernand0 Jan 16, 2024
0626410
add unit test around new behavior
zFernand0 Jan 16, 2024
40c6f13
i18n package updated after running package
JillieBeanSim Jan 16, 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"vscode": "^1.53.2"
},
"dependencies": {
"@zowe/cli": "7.21.1",
"@zowe/cli": "7.21.4",
"vscode-nls": "4.1.2"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions packages/zowe-explorer-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t

### New features and enhancements


zFernand0 marked this conversation as resolved.
Show resolved Hide resolved
- Added optional `openDs` function to `IZoweDatasetTreeNode` to open a data set or member in the editor.
- Added optional `setEncoding` function to `IZoweDatasetTreeNode` and `IZoweUSSTreeNode` to set the encoding of a node to binary, text, or a custom codepage.
- Added optional properties `binary`, `encoding`, and `encodingMap` to tree node interfaces for storing the codepage of a data set or USS file.
- Deprecated `IZoweUSSTreeNode.binaryFiles` and `IZoweUSSTreeNode.setBinary` in favor of `IZoweUSSTreeNode.encodingMap` and `IZoweUSSTreeNode.setEncoding`.
- Deprecated `ZoweTreeNode.binary`, `ZoweTreeNode.binaryFiles`, and `ZoweTreeNode.shortLabel`. These properties are not applicable for all tree nodes and should be defined in subclasses of `ZoweTreeNode` if necessary.
- Added new functions `loginWithBaseProfile` and `logoutWithBaseProfile` to provide extenders with the ability to automatically login to their respective services. [#2493](https://github.com/zowe/vscode-extension-for-zowe/pull/2493)
- Added new optional method `getCommonApi` to `ZoweExplorerApi.IApiRegisterClient` for enhanced typings in other Zowe Explorer APIs. [#2493](https://github.com/zowe/vscode-extension-for-zowe/pull/2493)

### Bug fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ function createProfInfoMock(profiles: Partial<zowe.imperative.IProfileLoaded>[])
knownArgs: Object.entries(profile.profile as object).map(([k, v]) => ({ argName: k, argValue: v as unknown })),
};
},
updateProperty: jest.fn(),
updateKnownProperty: jest.fn(),
isSecured: jest.fn(),
} as any;
}

Expand Down Expand Up @@ -339,6 +342,27 @@ describe("ProfilesCache", () => {
expect(profileNames).toEqual(["lpar1", "lpar2"]);
});

describe("updateBaseProfileFile Login/Logout", () => {
const updProfile = { tokenType: "apimlAuthenticationToken", tokenValue: "tokenValue" };

it("should update the base profile on login", async () => {
const profCache = new ProfilesCache(fakeLogger as unknown as zowe.imperative.Logger);
const mockProfInfo = createProfInfoMock([lpar1Profile, lpar2Profile]);
jest.spyOn(profCache, "getProfileInfo").mockResolvedValue(mockProfInfo);
await profCache.updateBaseProfileFileLogin(lpar1Profile as any, updProfile);

expect(mockProfInfo.updateProperty).toBeCalledTimes(2);
});
it("should update the base profile on login", async () => {
const profCache = new ProfilesCache(fakeLogger as unknown as zowe.imperative.Logger);
const mockProfInfo = createProfInfoMock([lpar1Profile, lpar2Profile]);
jest.spyOn(profCache, "getProfileInfo").mockResolvedValue(mockProfInfo);
await profCache.updateBaseProfileFileLogout(lpar1Profile as any);

expect(mockProfInfo.updateKnownProperty).toBeCalledTimes(2);
});
});

describe("fetchAllProfilesByType", () => {
it("should return array of profile objects for given type", async () => {
const profCache = new ProfilesCache(fakeLogger as unknown as zowe.imperative.Logger);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@
import * as vscode from "vscode";
import { Gui } from "../../../src/globals/Gui";
import { MessageSeverity, IZoweLogger } from "../../../src/logger/IZoweLogger";
import { IProfileLoaded } from "@zowe/imperative";
import { IProfileLoaded, Session } from "@zowe/imperative";
import { IPromptCredentialsOptions, ZoweVsCodeExtension } from "../../../src/vscode";
import { ProfilesCache, ZoweExplorerApi } from "../../../src";
import { imperative } from "@zowe/cli";
import { Login, Logout, imperative } from "@zowe/cli";

describe("ZoweVsCodeExtension", () => {
const fakeVsce = {
exports: "zowe",
packageJSON: { version: "1.0.1" },
} as vscode.Extension<unknown>;

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

Expand Down Expand Up @@ -176,6 +177,173 @@ describe("ZoweVsCodeExtension", () => {
});
});

describe("login and logout with base profiles", () => {
const testProfile = {
host: "dummy",
port: 1234,
};
const baseProfile = { name: "base", type: "base", profile: testProfile };
const serviceProfile: any = { name: "service", type: "service", profile: testProfile };
const allProfiles = [serviceProfile, baseProfile];
const testNode: any = {
setProfileToChoice: jest.fn(),
getProfile: jest.fn().mockReturnValue(serviceProfile),
};
const expectedSession = new Session({
hostname: "dummy",
password: "Password",
port: 1234,
tokenType: "apimlAuthenticationToken",
type: "token",
user: "Username",
});
const updProfile = { tokenType: "apimlAuthenticationToken", tokenValue: "tokenValue" };
const testRegister: any = {
getCommonApi: () => ({
login: jest.fn().mockReturnValue("tokenValue"),
logout: jest.fn(),
getTokenTypeName: () => "apimlAuthenticationToken",
}),
};
const testCache: any = {
allProfiles,
allExternalTypes: [],
fetchBaseProfile: jest.fn(),
loadNamedProfile: jest.fn().mockReturnValue({ profile: testProfile }),
updateBaseProfileFileLogin: jest.fn(),
updateBaseProfileFileLogout: jest.fn(),
getLoadedProfConfig: jest.fn().mockReturnValue({ profile: {} }),
getProfileInfo: jest.fn().mockReturnValue({
isSecured: jest.fn().mockReturnValue(false),
getAllProfiles: jest.fn().mockReturnValue(allProfiles),
mergeArgsForProfile: jest.fn().mockReturnValue({ knownArgs: [] }),
}),
refresh: jest.fn(),
};

beforeEach(() => {
jest.spyOn(ZoweVsCodeExtension as any, "profilesCache", "get").mockReturnValue(testCache);
jest.spyOn(vscode.extensions, "getExtension").mockReturnValueOnce(fakeVsce);
});

it("should not login if the base profile cannot be fetched", async () => {
testCache.fetchBaseProfile.mockResolvedValue(null);
await ZoweVsCodeExtension.loginWithBaseProfile("service");
expect(testCache.fetchBaseProfile).toHaveBeenCalledTimes(1);
expect(testCache.updateBaseProfileFileLogin).not.toHaveBeenCalled();
});
it("should not logout if the base profile cannot be fetched", async () => {
testCache.fetchBaseProfile.mockResolvedValue(null);
await ZoweVsCodeExtension.logoutWithBaseProfile("service");
expect(testCache.fetchBaseProfile).toHaveBeenCalledTimes(1);
expect(testCache.updateBaseProfileFileLogin).not.toHaveBeenCalled();
});
it("should login using the base profile given a simple profile name", async () => {
testCache.fetchBaseProfile.mockResolvedValue(baseProfile);
const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "getServiceProfileForAuthPurposes");
jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue(["user", "pass"]);
const loginSpy = jest.spyOn(Login, "apimlLogin").mockResolvedValue("tokenValue");

await ZoweVsCodeExtension.loginWithBaseProfile("service");

const testSession = new Session(JSON.parse(JSON.stringify(expectedSession.ISession)));
testSession.ISession.base64EncodedAuth = "dXNlcjpwYXNz";

expect(loginSpy).toHaveBeenCalledWith(testSession);
expect(testSpy).toHaveBeenCalledWith(testCache, "service");
expect(testCache.updateBaseProfileFileLogin).toHaveBeenCalledWith(baseProfile, updProfile, false);
});
it("should logout using the base profile given a simple profile name", async () => {
testCache.fetchBaseProfile.mockResolvedValue(baseProfile);
const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "getServiceProfileForAuthPurposes");
testSpy.mockResolvedValue({ profile: { ...testProfile, ...updProfile } });
const logoutSpy = jest.spyOn(Logout, "apimlLogout").mockImplementation(jest.fn());

await ZoweVsCodeExtension.logoutWithBaseProfile("service");

const testSession = new Session(JSON.parse(JSON.stringify(expectedSession.ISession)));
testSession.ISession.tokenValue = "tokenValue";
delete testSession.ISession.base64EncodedAuth;
delete testSession.ISession.user;
delete testSession.ISession.password;

expect(logoutSpy).toHaveBeenCalledWith(testSession);
expect(testSpy).toHaveBeenCalledWith(testCache, "service");
expect(testCache.updateBaseProfileFileLogout).toHaveBeenCalledWith(baseProfile);
});
it("should login using the service profile given a simple profile name", async () => {
testCache.fetchBaseProfile.mockResolvedValue(baseProfile);
const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "getServiceProfileForAuthPurposes");
const newServiceProfile = { ...serviceProfile, profile: { ...testProfile, tokenValue: "tokenValue", host: "service" } };
testSpy.mockResolvedValue(newServiceProfile);
jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue(["user", "pass"]);
const loginSpy = jest.spyOn(Login, "apimlLogin").mockResolvedValue("tokenValue");

await ZoweVsCodeExtension.loginWithBaseProfile("service");

const testSession = new Session(JSON.parse(JSON.stringify(expectedSession.ISession)));
testSession.ISession.hostname = "service";
testSession.ISession.base64EncodedAuth = "dXNlcjpwYXNz";

expect(loginSpy).toHaveBeenCalledWith(testSession);
expect(testSpy).toHaveBeenCalledWith(testCache, "service");
expect(testCache.updateBaseProfileFileLogin).toHaveBeenCalledWith(newServiceProfile, updProfile, true);
});
it("should logout using the service profile given a simple profile name", async () => {
testCache.fetchBaseProfile.mockResolvedValue(baseProfile);
const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "getServiceProfileForAuthPurposes");
const newServiceProfile = { ...serviceProfile, profile: { ...testProfile, ...updProfile, host: "service" } };
testSpy.mockResolvedValue(newServiceProfile);
const logoutSpy = jest.spyOn(Logout, "apimlLogout").mockImplementation(jest.fn());

await ZoweVsCodeExtension.logoutWithBaseProfile("service");

const testSession = new Session(JSON.parse(JSON.stringify(expectedSession.ISession)));
testSession.ISession.hostname = "service";
testSession.ISession.tokenValue = "tokenValue";
delete testSession.ISession.base64EncodedAuth;
delete testSession.ISession.user;
delete testSession.ISession.password;

expect(logoutSpy).toHaveBeenCalledWith(testSession);
expect(testSpy).toHaveBeenCalledWith(testCache, "service");
expect(testCache.updateBaseProfileFileLogout).toHaveBeenCalledWith(newServiceProfile);
});
it("should login using the base profile when provided with a node, register, and cache instance", async () => {
testCache.fetchBaseProfile.mockResolvedValue(baseProfile);
const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "getServiceProfileForAuthPurposes");
jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue(["user", "pass"]);
const loginSpy = jest.spyOn(Login, "apimlLogin").mockResolvedValue("tokenValue");

await ZoweVsCodeExtension.loginWithBaseProfile(serviceProfile, "apimlAuthenticationToken", testNode, testRegister, testCache);

const testSession = new Session(JSON.parse(JSON.stringify(expectedSession.ISession)));
testSession.ISession.base64EncodedAuth = "dXNlcjpwYXNz";

expect(loginSpy).not.toHaveBeenCalled();
expect(testSpy).not.toHaveBeenCalled();
expect(testCache.updateBaseProfileFileLogin).toHaveBeenCalledWith(baseProfile, updProfile, false);
expect(testNode.setProfileToChoice).toHaveBeenCalled();
});
it("should logout using the base profile when provided with a node, register, and cache instance", async () => {
testCache.fetchBaseProfile.mockResolvedValue(baseProfile);
const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "getServiceProfileForAuthPurposes");
const logoutSpy = jest.spyOn(Logout, "apimlLogout").mockImplementation(jest.fn());
const newServiceProfile = { ...serviceProfile, profile: { ...testProfile, ...updProfile } };

await ZoweVsCodeExtension.logoutWithBaseProfile(newServiceProfile, testRegister, testCache);

const testSession = new Session(JSON.parse(JSON.stringify(expectedSession.ISession)));
testSession.ISession.tokenValue = "tokenValue";
delete testSession.ISession.base64EncodedAuth;
delete testSession.ISession.user;
delete testSession.ISession.password;

expect(logoutSpy).not.toHaveBeenCalled();
expect(testSpy).not.toHaveBeenCalled();
expect(testCache.updateBaseProfileFileLogout).toHaveBeenCalledWith(baseProfile);
});
});
describe("updateCredentials", () => {
const promptCredsOptions: IPromptCredentialsOptions = {
sessionName: "test",
Expand Down
2 changes: 1 addition & 1 deletion packages/zowe-explorer-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"dependencies": {
"@types/vscode": "^1.53.2",
"@zowe/cli": "7.21.1",
"@zowe/cli": "7.21.4",
"@zowe/secrets-for-zowe-sdk": "7.18.6",
"handlebars": "^4.7.7",
"semver": "^7.5.3"
Expand Down
23 changes: 22 additions & 1 deletion packages/zowe-explorer-api/src/profiles/ProfilesCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export class ProfilesCache {
let mProfileInfo: zowe.imperative.ProfileInfo;
try {
mProfileInfo = await this.getProfileInfo();
const allTypes = this.getAllProfileTypes(apiRegister.registeredApiTypes());
const allTypes = this.getAllProfileTypes(apiRegister?.registeredApiTypes() ?? []);
allTypes.push("base");
for (const type of allTypes) {
const tmpAllProfiles: zowe.imperative.IProfileLoaded[] = [];
Expand Down Expand Up @@ -548,4 +548,25 @@ export class ProfilesCache {
profile?.profile.tokenType === zowe.imperative.SessConstants.TOKEN_TYPE_APIML
zFernand0 marked this conversation as resolved.
Show resolved Hide resolved
);
}

public async updateBaseProfileFileLogin(
profile: zowe.imperative.IProfileLoaded,
updProfile: zowe.imperative.IProfile,
forceUpdate?: boolean
): Promise<void> {
const upd = { profileName: profile.name, profileType: profile.type };
const mProfileInfo = await this.getProfileInfo();
const setSecure = mProfileInfo.isSecured();
await mProfileInfo.updateProperty({ ...upd, property: "tokenType", value: updProfile.tokenType, forceUpdate });
await mProfileInfo.updateProperty({ ...upd, property: "tokenValue", value: updProfile.tokenValue, setSecure, forceUpdate });
}

public async updateBaseProfileFileLogout(profile: zowe.imperative.IProfileLoaded): Promise<void> {
const mProfileInfo = await this.getProfileInfo();
const setSecure = mProfileInfo.isSecured();
const prof = mProfileInfo.getAllProfiles(profile.type).find((p) => p.profName === profile.name);
const mergedArgs = mProfileInfo.mergeArgsForProfile(prof);
await mProfileInfo.updateKnownProperty({ mergedArgs, property: "tokenValue", value: undefined, setSecure });
await mProfileInfo.updateKnownProperty({ mergedArgs, property: "tokenType", value: undefined });
}
}
7 changes: 7 additions & 0 deletions packages/zowe-explorer-api/src/profiles/ZoweExplorerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,5 +670,12 @@ export namespace ZoweExplorerApi {
* Define events that fire whenever an existing team config profile is updated.
*/
onProfilesUpdate?: vscode.Event<EventTypes>;

/**
* Lookup of any registered API (Uss, Mvs, Jes, or Command).
* @param {string} profile
* @returns the registered API instance
*/
getCommonApi?(profile: zowe.imperative.IProfileLoaded): ZoweExplorerApi.ICommon;
}
}
5 changes: 3 additions & 2 deletions packages/zowe-explorer-api/src/utils/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ export type FileAttributes = {
export function permStringToOctal(perms: string): number {
const permsWithoutDirFlag = perms.substring(1);
let octalValue = "";
for (let i = 0; i + 3 <= permsWithoutDirFlag.length; i += 3) {
const group = permsWithoutDirFlag.slice(i, i + 3);
const offset = 3;
for (let i = 0; i + offset <= permsWithoutDirFlag.length; i += offset) {
const group = permsWithoutDirFlag.slice(i, i + offset);
let groupValue = 0;
for (const char of group) {
if (char in PERM_VALUES) {
Expand Down
Loading
Loading