Skip to content

Commit

Permalink
Merge branch 'master' into update-next
Browse files Browse the repository at this point in the history
  • Loading branch information
t1m0thyj committed Jul 23, 2024
2 parents 4bb4735 + ce2ede7 commit f33275f
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 17 deletions.
8 changes: 8 additions & 0 deletions packages/imperative/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to the Imperative package will be documented in this file.

## Recent Changes

- Enhancement: Updated `ProfileInfo.updateProperty` function to support updating properties in typeless profiles. [#2196](https://github.com/zowe/zowe-cli/issues/2196)

## `8.0.0-next.202407181904`

- Enhancement: Added the function ConfigUtils.formGlobOrProjProfileNm and modified the function ConfigBuilder.build so that the 'zowe config init' command now generates a base profile name of 'global_base' or 'project_base', depending on whether a global or project configuration file is being generated. Related to Zowe Explorer issue https://github.com/zowe/zowe-explorer-vscode/issues/2682.
Expand Down Expand Up @@ -418,6 +422,10 @@ All notable changes to the Imperative package will be documented in this file.

- Major: First major version bump for V3

## `5.26.0`

- Enhancement: Updated `ProfileInfo.updateProperty` function to support updating properties in typeless profiles. [#2196](https://github.com/zowe/zowe-cli/issues/2196)

## `5.25.0`

- Enhancement: Added `ProfileInfo.profileManagerWillLoad` function to verify the credential manager can load. [#2111](https://github.com/zowe/zowe-cli/issues/2111)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,7 @@ describe("TeamConfig ProfileInfo tests", () => {
});

describe("updateProperty", () => {
it("should throw and error if the desired profile is not found", async () => {
it("should throw an error if the desired profile is not found", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
jest.spyOn(profInfo as any, "getAllProfiles").mockReturnValue([]);
Expand Down Expand Up @@ -1175,6 +1175,76 @@ describe("TeamConfig ProfileInfo tests", () => {
profileType: "dummy"
});
});

it("should succeed storing property in typeless profile if profile exists", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const storeSpy = jest.spyOn(ConfigAutoStore, "_storeSessCfgProps").mockImplementation(jest.fn());
const profileOptions: IProfInfoUpdatePropOpts = {
profileName: "typeless",
profileType: null,
property: "areBirdsReal",
value: true
};
let caughtError;
try {
await profInfo.updateProperty(profileOptions);
} catch (error) {
caughtError = error;
}
expect(caughtError).toBeUndefined();
expect(storeSpy).toHaveBeenCalledWith({
config: profInfo.getTeamConfig(), profileName: "typeless", profileType: null,
defaultBaseProfileName: "base_glob",
propsToStore: [ "areBirdsReal" ], sessCfg: { "areBirdsReal": true }, setSecure : undefined,
});
});

it("should fail to store property in typeless profile if profile doesn't exist", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profileOptions: IProfInfoUpdatePropOpts = {
profileName: "typeless_new",
profileType: null,
property: "areBirdsReal",
value: true
};
let caughtError;
try {
await profInfo.updateProperty(profileOptions);
} catch (error) {
caughtError = error;
}

expect(caughtError).toBeDefined();
expect(caughtError.errorCode).toBe(ProfInfoErr.PROF_NOT_FOUND);
expect(caughtError.message).toContain("Failed to find profile");
});

it("should succeed storing property in typeless profile if profile doesn't exist and forceUpdate is true", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const storeSpy = jest.spyOn(ConfigAutoStore, "_storeSessCfgProps").mockImplementation(jest.fn());
const profileOptions: IProfInfoUpdatePropOpts = {
profileName: "typeless_new",
profileType: null,
property: "areBirdsReal",
value: true,
forceUpdate: true
};
let caughtError;
try {
await profInfo.updateProperty(profileOptions);
} catch (error) {
caughtError = error;
}
expect(caughtError).toBeUndefined();
expect(storeSpy).toHaveBeenCalledWith({
config: profInfo.getTeamConfig(), profileName: "typeless_new", profileType: null,
defaultBaseProfileName: "base_glob",
propsToStore: [ "areBirdsReal" ], sessCfg: { "areBirdsReal": true }, setSecure : undefined,
});
});
});

describe("updateKnownProperty", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
"responseFormatHeader": true,
"fakeOffSchemaArg": "fakeArg"
}
},
"typeless": {
"properties": {}
}
},
"defaults": {
Expand Down
1 change: 1 addition & 0 deletions packages/imperative/src/config/src/ConfigAutoStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export class ConfigAutoStore {
const config = opts.config || ImperativeConfig.instance.config;
// TODO Which autoStore value should take priority if it conflicts between layers
if (opts.propsToStore.length == 0 || !config?.exists || !config.properties.autoStore) {
Logger.getAppLogger().info("Skipping update of profile properties. Check that config file exists and autoStore is true.");
return;
}

Expand Down
30 changes: 17 additions & 13 deletions packages/imperative/src/config/src/ProfileInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,25 +185,29 @@ export class ProfileInfo {
*/
public async updateProperty(options: IProfInfoUpdatePropOpts): Promise<void> {
this.ensureReadFromDisk();
const desiredProfile = this.getAllProfiles(options.profileType).find(v => v.profName === options.profileName);
if (desiredProfile == null) {
const desiredProfile = options.profileType != null ?
this.getAllProfiles(options.profileType).find(v => v.profName === options.profileName) : null;
let updated = false;
if (desiredProfile != null) {
const mergedArgs = this.mergeArgsForProfile(desiredProfile, { getSecureVals: false });
if (options.forceUpdate) {
const knownProperty = mergedArgs.knownArgs.find((v => v.argName === options.property));
if (knownProperty != null) {
const profPath = this.getTeamConfig().api.profiles.getProfilePathFromName(options.profileName);
if (!ConfigUtils.jsonPathMatches(knownProperty.argLoc.jsonLoc, profPath)) {
knownProperty.argLoc.jsonLoc = `${profPath}.properties.${options.property}`;
}
}
}
updated = await this.updateKnownProperty({ ...options, mergedArgs, osLocInfo: this.getOsLocInfo(desiredProfile)?.[0] });
} else if (!(options.profileType == null && (options.forceUpdate || this.getTeamConfig().api.profiles.exists(options.profileName)))) {
throw new ProfInfoErr({
errorCode: ProfInfoErr.PROF_NOT_FOUND,
msg: `Failed to find profile ${options.profileName} of type ${options.profileType}`
});
}

const mergedArgs = this.mergeArgsForProfile(desiredProfile, { getSecureVals: false });
if (options.forceUpdate) {
const knownProperty = mergedArgs.knownArgs.find(v => v.argName === options.property);
if (knownProperty != null) {
const profPath = this.getTeamConfig().api.profiles.getProfilePathFromName(options.profileName);
if (!ConfigUtils.jsonPathMatches(knownProperty.argLoc.jsonLoc, profPath)) {
knownProperty.argLoc.jsonLoc = `${profPath}.properties.${options.property}`;
}
}
}
if (!await this.updateKnownProperty({ ...options, mergedArgs, osLocInfo: this.getOsLocInfo(desiredProfile)?.[0] })) {
if (!updated) {
// Check to see if loadedConfig already contains the schema for the specified profile type
if (ImperativeConfig.instance.loadedConfig?.profiles?.find(p => p.type === options.profileType)?.schema == null ||
ImperativeConfig.instance.loadedConfig?.baseProfile?.schema == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ import { IProfMergedArg } from "./IProfMergedArg";
*/
export interface IProfInfoUpdatePropOpts extends IProfInfoUpdatePropCommonOpts {
/**
* Type of the active profile
* Type of the active profile.
* Specify `null` to update a typeless profile.
*/
profileType: string;
profileType: string | null;

/**
* Name of the active profile
*/
profileName: string;

/**
* Force the update to the profile specified even if the property comes from somehwere else
* Force the update to the profile specified even if the property comes from somewhere else
* @example Token Value could be in the base profile (not in the service profile specified)
* and the programmer has the intention of storing the token in the service profile
* @default false When the property is not specified, the updateProperty method follows current
Expand Down

0 comments on commit f33275f

Please sign in to comment.