From 48f7d879273e655ca7cc57c1985b964916921e5e Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 3 Feb 2025 15:38:47 -0500 Subject: [PATCH 01/16] like named members functionality for PDSs Signed-off-by: Pujal --- .../__unit__/copy/ds/Ds.handler.unit.test.ts | 89 +++++++++++-- .../cli/src/zosfiles/copy/ds/Ds.handler.ts | 15 ++- .../methods/copy/Copy.system.test.ts | 34 +++++ .../__unit__/methods/copy/Copy.unit.test.ts | 117 +++++++++++++++++- .../src/constants/ZosFiles.messages.ts | 1 - packages/zosfiles/src/methods/copy/Copy.ts | 27 ++++ .../methods/copy/doc/ICopyDatasetOptions.ts | 6 + 7 files changed, 277 insertions(+), 12 deletions(-) diff --git a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts index 2975ad862..a29a4bd79 100644 --- a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts +++ b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts @@ -50,7 +50,7 @@ describe("DsHandler", () => { }, response: { - console: { promptFn: jest.fn() } + console: { promptFn: jest.fn(), promptForLikeNamedMembers: jest.fn() } } }; @@ -68,7 +68,8 @@ describe("DsHandler", () => { "replace": commandParameters.arguments.replace, "responseTimeout": commandParameters.arguments.responseTimeout, "safeReplace": commandParameters.arguments.safeReplace, - "promptFn": expect.any(Function) + "promptFn": expect.any(Function), + "promptForLikeNamedMembers": expect.any(Function) } ); expect(response).toBe(defaultReturn); @@ -98,7 +99,7 @@ describe("DsHandler", () => { responseTimeout }, response: { - console: { promptFn: jest.fn() } + console: { promptFn: jest.fn(), promptForLikeNamedMembers: jest.fn() } } }; @@ -116,7 +117,8 @@ describe("DsHandler", () => { "replace": commandParameters.arguments.replace, "responseTimeout": commandParameters.arguments.responseTimeout, "safeReplace": commandParameters.arguments.safeReplace, - "promptFn": expect.any(Function) + "promptFn": expect.any(Function), + "promptForLikeNamedMembers": expect.any(Function) } ); expect(response).toBe(defaultReturn); @@ -162,7 +164,8 @@ describe("DsHandler", () => { "replace": commandParameters.arguments.replace, "responseTimeout": commandParameters.arguments.responseTimeout, "safeReplace": commandParameters.arguments.safeReplace, - "promptFn": expect.any(Function) + "promptFn": expect.any(Function), + "promptForLikeNamedMembers": expect.any(Function) } ); expect(response).toBe(defaultReturn); @@ -198,7 +201,7 @@ describe("DsHandler", () => { const result = await promptFn(commandParameters.arguments.toDataSetName); expect(promptMock).toHaveBeenCalledWith( - `The dataset '${toDataSetName}' exists on the target system. This copy will result in data loss.` + + `The dataset '${toDataSetName}' exists on the target system. This copy may result in data loss.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(true); @@ -234,7 +237,79 @@ describe("DsHandler", () => { const result = await promptFn(commandParameters.arguments.toDataSetName); expect(promptMock).toHaveBeenCalledWith( - `The dataset '${toDataSetName}' exists on the target system. This copy will result in data loss.` + + `The dataset '${toDataSetName}' exists on the target system. This copy may result in data loss.` + + ` Are you sure you want to continue? [y/N]: ` + ); + expect(result).toBe(false); + }); + it("should prompt the user about duplicate member names and return true when input is 'y", async () => { + const handler = new DsHandler(); + + expect(handler).toBeInstanceOf(ZosFilesBaseHandler); + const fromDataSetName = "ABCD"; + const toDataSetName = "EFGH"; + const enq = "SHR"; + const replace = false; + const safeReplace = false; + const responseTimeout: any = undefined; + + const commandParameters: any = { + arguments: { + fromDataSetName, + toDataSetName, + enq, + replace, + safeReplace, + responseTimeout + }, + response: { + console: { promptFn: jest.fn() } + } + }; + const promptMock = jest.fn(); + promptMock.mockResolvedValue("y"); + + const promptForDuplicates = (handler as any)["promptForLikeNamedMembers"]({ prompt: promptMock }); + const result = await promptForDuplicates(); + + expect(promptMock).toHaveBeenCalledWith( + `The source and target data sets have like named member names. The contents of those members will be overwritten.` + + ` Are you sure you want to continue? [y/N]: ` + ); + expect(result).toBe(true); + }); + it("should prompt the user about duplicate member names and return false when input is 'N'", async () => { + const handler = new DsHandler(); + + expect(handler).toBeInstanceOf(ZosFilesBaseHandler); + const fromDataSetName = "ABCD"; + const toDataSetName = "EFGH"; + const enq = "SHR"; + const replace = false; + const safeReplace = false; + const responseTimeout: any = undefined; + + const commandParameters: any = { + arguments: { + fromDataSetName, + toDataSetName, + enq, + replace, + safeReplace, + responseTimeout + }, + response: { + console: { promptFn: jest.fn() } + } + }; + const promptMock = jest.fn(); + promptMock.mockResolvedValue("N"); + + const promptForDuplicates = (handler as any)["promptForLikeNamedMembers"]({ prompt: promptMock }); + const result = await promptForDuplicates(); + + expect(promptMock).toHaveBeenCalledWith( + `The source and target data sets have like named member names. The contents of those members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(false); diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts index 40913a7d5..2f1666839 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts @@ -26,7 +26,8 @@ export default class DsHandler extends ZosFilesBaseHandler { replace: commandParameters.arguments.replace, responseTimeout: commandParameters.arguments.responseTimeout, safeReplace: commandParameters.arguments.safeReplace, - promptFn: this.promptForSafeReplace(commandParameters.response.console) + promptFn: this.promptForSafeReplace(commandParameters.response.console), + promptForLikeNamedMembers: this.promptForLikeNamedMembers(commandParameters.response.console) }; return Copy.dataSet(session, toDataSet, options); @@ -35,10 +36,20 @@ export default class DsHandler extends ZosFilesBaseHandler { private promptForSafeReplace(console: IHandlerResponseConsoleApi) { return async (targetDSN: string) => { const answer: string = await console.prompt( - `The dataset '${targetDSN}' exists on the target system. This copy will result in data loss.` + + `The dataset '${targetDSN}' exists on the target system. This copy may result in data loss.` + ` Are you sure you want to continue? [y/N]: ` ); return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); }; } + + private promptForLikeNamedMembers(console: IHandlerResponseConsoleApi) { + return async() => { + const answer: string = await console.prompt ( + `The source and target data sets have like named member names. The contents of those members will be overwritten.` + + ` Are you sure you want to continue? [y/N]: ` + ) + return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); + }; + } } diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index 859457672..ca3a78cae 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -598,6 +598,40 @@ describe("Copy", () => { }); }); + describe("hasLikeNamedMembers", () => { + beforeEach(async () => { + try { + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_PARTITIONED, fromDataSetName); + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_PARTITIONED, toDataSetName); + await Upload.fileToDataset(REAL_SESSION, fileLocation, fromDataSetName); + await Upload.fileToDataset(REAL_SESSION, fileLocation, toDataSetName); + } + catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + }); + afterEach(async () => { + try { + await Delete.dataSet(REAL_SESSION, fromDataSetName); + await Delete.dataSet(REAL_SESSION, toDataSetName); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + }); + it("should return true if the source and target data sets have like-named members", async () => { + const response = await Copy["hasLikeNamedMembers"](REAL_SESSION, fromDataSetName, toDataSetName); + expect(response).toBe(true); + }); + + it("should return false if the source and target data sets do not have like-named members", async () => { + await Delete.dataSet(REAL_SESSION, toDataSetName); + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_PARTITIONED, toDataSetName); + + const response = await Copy["hasLikeNamedMembers"](REAL_SESSION, fromDataSetName, toDataSetName); + expect(response).toBe(false); + }); + }); + describe("Data Set Cross LPAR", () => { describe("Common Failures", () => { it("should fail if no fromDataSet data set name is supplied", async () => { diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index 2ecad85e7..0772a1141 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -35,15 +35,18 @@ describe("Copy", () => { const toDataSetName = "USER.DATA.TO"; const toMemberName = "mem2"; const isPDSSpy = jest.spyOn(Copy as any, "isPDS"); + const hasLikeNamedMembers = jest.spyOn(Copy as any, "hasLikeNamedMembers"); let dataSetExistsSpy: jest.SpyInstance; const promptFn = jest.fn(); + const promptForLikeNamedMembers = jest.fn(); beforeEach(() => { copyPDSSpy.mockClear(); copyExpectStringSpy.mockClear().mockImplementation(async () => { return ""; }); isPDSSpy.mockClear().mockResolvedValue(false); dataSetExistsSpy = jest.spyOn(Copy as any, "dataSetExists").mockResolvedValue(true); - + hasLikeNamedMembers.mockClear().mockResolvedValue(false); + promptForLikeNamedMembers.mockClear(); }); afterAll(() => { isPDSSpy.mockRestore(); @@ -619,6 +622,48 @@ describe("Copy", () => { commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message }); }); + it("should display a prompt for like named members if there are duplicate member names and --safe-replace and --replace flags are not used", async () => { + hasLikeNamedMembers.mockResolvedValue(true); + promptForLikeNamedMembers.mockClear().mockResolvedValue(true); + + const response = await Copy.dataSet( + dummySession, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName }, + safeReplace: false, + replace: false, + promptForLikeNamedMembers } + ); + expect(promptForLikeNamedMembers).toHaveBeenCalledWith(); + + }) + it("should not display a prompt for like named members if there are no duplicate member names", async () => { + const response = await Copy.dataSet( + dummySession, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName }, + safeReplace: false, + replace: false, + promptForLikeNamedMembers } + ); + + expect(promptForLikeNamedMembers).not.toHaveBeenCalled(); + }); + it("should throw error if user declines to replace the dataset", async () => { + hasLikeNamedMembers.mockResolvedValue(true); + promptForLikeNamedMembers.mockClear().mockResolvedValue(false); + + await expect(Copy.dataSet( + dummySession, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName }, + safeReplace: false, + replace: false, + promptForLikeNamedMembers } + )).rejects.toThrow(new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message })); + + expect(promptForLikeNamedMembers).toHaveBeenCalled(); + }); }); it("should return early if the source and target data sets are identical", async () => { const response = await Copy.dataSet( @@ -711,7 +756,7 @@ describe("Copy", () => { }); }); - describe("Copy Partitioned Data Set", () => { + describe("Partitioned Data Set", () => { const listAllMembersSpy = jest.spyOn(List, "allMembers"); const downloadAllMembersSpy = jest.spyOn(Download, "allMembers"); const uploadSpy = jest.spyOn(Upload, "streamToDataSet"); @@ -722,6 +767,11 @@ describe("Copy", () => { const readStream = jest.spyOn(IO, "createReadStream"); const rmSync = jest.spyOn(fs, "rmSync"); const listDatasetSpy = jest.spyOn(List, "dataSet"); + const hasLikeNamedMembers = jest.spyOn(Copy as any, "hasLikeNamedMembers"); + + beforeEach(() => { + hasLikeNamedMembers.mockRestore(); + }); const dsPO = { dsname: fromDataSetName, @@ -849,6 +899,69 @@ describe("Copy", () => { commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message, }); }); + + describe("hasLikeNamedMembers", () => { + const listAllMembersSpy = jest.spyOn(List, "allMembers"); + + beforeEach(() => { + jest.clearAllMocks(); + }); + it("should return true if the source and target have like-named members", async () => { + listAllMembersSpy.mockImplementation(async (session, dsName): Promise => { + if (dsName === fromDataSetName) { + return { + apiResponse: { + items: [ + { member: "mem1" }, + { member: "mem2" }, + ] + } + }; + } else if (dsName === toDataSetName) { + return { + apiResponse: { + items: [{ member: "mem1" }] + } + }; + } + }); + + const response = await Copy["hasLikeNamedMembers"](dummySession, fromDataSetName, toDataSetName); + expect(response).toBe(true); + expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, fromDataSetName); + expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, toDataSetName); + }); + it("should return false if the source and target do not have like-named members", async () => { + const sourceResponse = { + apiResponse: { + items: [ + { member: "mem1" }, + { member: "mem2" }, + ] + } + }; + const targetResponse = { + apiResponse: { + items: [ + { member: "mem3" }, + ] + } + }; + listAllMembersSpy.mockImplementation(async (session, dsName): Promise => { + if (dsName === fromDataSetName) { + return sourceResponse; + } else if (dsName === toDataSetName) { + return targetResponse; + } + }); + + const response = await Copy["hasLikeNamedMembers"](dummySession, fromDataSetName, toDataSetName); + + expect(response).toBe(false); + expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, fromDataSetName); + expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, toDataSetName); + }); + }); }); describe("Data Set Cross LPAR", () => { diff --git a/packages/zosfiles/src/constants/ZosFiles.messages.ts b/packages/zosfiles/src/constants/ZosFiles.messages.ts index 9c4b9d8e7..f14887729 100644 --- a/packages/zosfiles/src/constants/ZosFiles.messages.ts +++ b/packages/zosfiles/src/constants/ZosFiles.messages.ts @@ -189,7 +189,6 @@ export const ZosFilesMessages: { [key: string]: IMessageDefinition } = { message: "Member(s) downloaded successfully." }, - /** * Message indicating that the member was downloaded successfully * @type {IMessageDefinition} diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 2894cc5c4..8ecfdd03f 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -31,6 +31,7 @@ import { ZosFilesUtils } from "../../utils/ZosFilesUtils"; import { tmpdir } from "os"; import path = require("path"); import * as util from "util"; +import { has } from "lodash"; /** * This class holds helper functions that are used to copy the contents of datasets through the * z/OSMF APIs. @@ -58,6 +59,7 @@ export class Copy { ImperativeExpect.toBeDefinedAndNonBlank(options["from-dataset"].dsn, "fromDataSetName"); ImperativeExpect.toBeDefinedAndNonBlank(toDataSetName, "toDataSetName"); const safeReplace: boolean = options.safeReplace; + const overwriteMembers: boolean = options.replace; if(options["from-dataset"].dsn === toDataSetName && toMemberName === options["from-dataset"].member) { return { @@ -84,7 +86,16 @@ export class Copy { if(!toMemberName && !options["from-dataset"].member) { const sourceIsPds = await this.isPDS(session, options["from-dataset"].dsn); const targetIsPds = await this.isPDS(session, toDataSetName); + if (sourceIsPds && targetIsPds) { + const hasLikeNamedMembers = await this.hasLikeNamedMembers(session, options["from-dataset"].dsn, toDataSetName); + if(!safeReplace && hasLikeNamedMembers && !overwriteMembers) { + const userResponse = await options.promptForLikeNamedMembers(); + + if(!userResponse) { + throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message }); + } + } const response = await this.copyPDS(session, options["from-dataset"].dsn, toDataSetName); return { success: true, @@ -170,6 +181,22 @@ export class Copy { return dsnameIndex !== -1; } + /** + * Function that checks if source and target data sets have like-named members + */ + private static async hasLikeNamedMembers ( + session: AbstractSession, + fromPds: string, + toPds: string + ): Promise { + const sourceResponse = await List.allMembers(session, fromPds); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); + const targetResponse = await List.allMembers(session, toPds); + const targetMemberList = targetResponse.apiResponse.items.map((item: { member: any; }) => item.member); + + return sourceMemberList.some((mem: any) => targetMemberList.includes(mem)); + } + /** * Copy the members of a Partitioned dataset into another Partitioned dataset * diff --git a/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts b/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts index 23d75b796..948f69bfb 100644 --- a/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts +++ b/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts @@ -47,4 +47,10 @@ export interface ICopyDatasetOptions extends IZosFilesOptions { * @returns True if target data set should be overwritten */ promptFn?: (targetDSN: string) => Promise; + + /** + * Prompt for duplicates + * @returns True if target data set members + */ + promptForLikeNamedMembers?: () => Promise; } From 1575d467cb66801cb41ac6305ec6c0ecee9f4efe Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 3 Feb 2025 15:41:27 -0500 Subject: [PATCH 02/16] updates Signed-off-by: Pujal --- packages/zosfiles/src/methods/copy/Copy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 8ecfdd03f..889998f46 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -93,7 +93,7 @@ export class Copy { const userResponse = await options.promptForLikeNamedMembers(); if(!userResponse) { - throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message }); + throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message}); } } const response = await this.copyPDS(session, options["from-dataset"].dsn, toDataSetName); From 3d24f6dcbbad8e66e29e51d23f5f189d025c9822 Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 3 Feb 2025 15:53:11 -0500 Subject: [PATCH 03/16] changelogs Signed-off-by: Pujal --- packages/cli/CHANGELOG.md | 1 + packages/zosfiles/CHANGELOG.md | 1 + .../zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts | 2 +- packages/zosfiles/src/methods/copy/Copy.ts | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 299262a88..153ba64f2 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to the Zowe CLI package will be documented in this file. ## Recent Changes +- Enhancement: When copying PDSs with like-named members, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added `--recordRange` flag to `zowe jobs download output` command to allow users to select a specific range of records to output from a spool file. [#2411](https://github.com/zowe/zowe-cli/pull/2411) - BugFix: The `zowe zos-files copy data-set` command overwrites the contents of the target data set without user confirmation. A `--safe-replace` option was added which prompts the user to confirm before overwriting the contents of the target data set. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 2a6e4353a..609287225 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes +- Enhancement: When copying PDSs with like-named members, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index 0772a1141..22493596f 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -636,7 +636,7 @@ describe("Copy", () => { ); expect(promptForLikeNamedMembers).toHaveBeenCalledWith(); - }) + }); it("should not display a prompt for like named members if there are no duplicate member names", async () => { const response = await Copy.dataSet( dummySession, diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 889998f46..8b1c085d3 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -31,7 +31,6 @@ import { ZosFilesUtils } from "../../utils/ZosFilesUtils"; import { tmpdir } from "os"; import path = require("path"); import * as util from "util"; -import { has } from "lodash"; /** * This class holds helper functions that are used to copy the contents of datasets through the * z/OSMF APIs. From 471e754f0c2f9f265a03932f698342dc287c03e2 Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 3 Feb 2025 16:06:35 -0500 Subject: [PATCH 04/16] updated unit tests Signed-off-by: Pujal --- .../zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index 22493596f..dc16779c4 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -635,6 +635,7 @@ describe("Copy", () => { promptForLikeNamedMembers } ); expect(promptForLikeNamedMembers).toHaveBeenCalledWith(); + expect(response.success).toEqual(true); }); it("should not display a prompt for like named members if there are no duplicate member names", async () => { @@ -646,7 +647,7 @@ describe("Copy", () => { replace: false, promptForLikeNamedMembers } ); - + expect(response.success).toEqual(true); expect(promptForLikeNamedMembers).not.toHaveBeenCalled(); }); it("should throw error if user declines to replace the dataset", async () => { From 9911d183e6581548c0014c0ff53700d79a4b444e Mon Sep 17 00:00:00 2001 From: Pujal Date: Wed, 5 Feb 2025 11:46:47 -0500 Subject: [PATCH 05/16] updated like-named terminology to identical member names Signed-off-by: Pujal --- packages/cli/CHANGELOG.md | 4 +-- ...cli.files.copy.ds.integration.test.ts.snap | 14 +++++----- .../__unit__/copy/ds/Ds.handler.unit.test.ts | 4 +-- .../Ds.definition.unit.test.ts.snap | 6 ++--- packages/cli/src/zosfiles/-strings-/en.ts | 6 ++--- .../cli/src/zosfiles/copy/ds/Ds.handler.ts | 2 +- packages/zosfiles/CHANGELOG.md | 2 +- .../methods/copy/Copy.system.test.ts | 10 +++---- .../__unit__/methods/copy/Copy.unit.test.ts | 27 ++++++++++--------- packages/zosfiles/src/methods/copy/Copy.ts | 8 +++--- 10 files changed, 42 insertions(+), 41 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 153ba64f2..516428ff6 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe CLI package will be documented in this file. ## Recent Changes -- Enhancement: When copying PDSs with like-named members, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- Enhancement: When copying PDSs that have identical member names, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added `--recordRange` flag to `zowe jobs download output` command to allow users to select a specific range of records to output from a spool file. [#2411](https://github.com/zowe/zowe-cli/pull/2411) - BugFix: The `zowe zos-files copy data-set` command overwrites the contents of the target data set without user confirmation. A `--safe-replace` option was added which prompts the user to confirm before overwriting the contents of the target data set. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) @@ -932,7 +932,7 @@ LTS Breaking: Removed the following previously deprecated items: [#1981](https:/ ## `6.25.0` -- Enhancement: Added a `--replace` option to the `zowe zos-files copy data-set` command. Use this option if you want to replace like-named members in the target data set. [#808](https://github.com/zowe/zowe-cli/issues/808) +- Enhancement: Added a `--replace` option to the `zowe zos-files copy data-set` command. Use this option if you want to replace members with identical names in the target data set. [#808](https://github.com/zowe/zowe-cli/issues/808) - Enhancement: Improved a cryptic error message that was shown if TSO address space failed to start for the `zowe zos-tso issue command` command. [#28](https://github.com/zowe/zowe-cli/issues/28) - Bugfix: Removed "[object Object]" text that appeared in some error messages. The proper text "Imperative API Error" is now displayed. [#836](https://github.com/zowe/zowe-cli/pull/836) diff --git a/packages/cli/__tests__/zosfiles/__integration__/copy/ds/__snapshots__/cli.files.copy.ds.integration.test.ts.snap b/packages/cli/__tests__/zosfiles/__integration__/copy/ds/__snapshots__/cli.files.copy.ds.integration.test.ts.snap index 649eac42b..a250cb8c9 100644 --- a/packages/cli/__tests__/zosfiles/__integration__/copy/ds/__snapshots__/cli.files.copy.ds.integration.test.ts.snap +++ b/packages/cli/__tests__/zosfiles/__integration__/copy/ds/__snapshots__/cli.files.copy.ds.integration.test.ts.snap @@ -34,13 +34,13 @@ exports[`Copy Data Set should display the help 1`] = ` --replace | --rep (boolean) - Specify this option as true if you wish to replace like-named members in the - target data set + Specify this option as true if you wish to replace members with identical names + in the target data set --safe-replace | --safe-rep | --sr (boolean) - Specify this option as true if you wish to replace like-named members or the - content of the target data set. This option will prompt to confirm. + Specify this option as true if you wish to replace members with identical names + or the content of the target data set. This option will prompt to confirm. --response-timeout | --rto (number) @@ -162,7 +162,7 @@ exports[`Copy Data Set should display the help 1`] = ` $ zowe zos-files copy data-set \\"USER.FROM.SET(mem1)\\" \\"USER.TO.SET\\" - Copy the data set named 'USER.FROM.SET' to the data set - named 'USER.TO.SET' and replace like-named members: + named 'USER.TO.SET' and replace members with identical names: $ zowe zos-files copy data-set \\"USER.FROM.SET\\" \\"USER.TO.SET\\" --replace @@ -180,8 +180,8 @@ exports[`Copy Data Set should display the help in json format 1`] = ` \\"success\\": true, \\"exitCode\\": 0, \\"message\\": \\"The help was constructed for command: data-set.\\", - \\"stdout\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n data-set | ds\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Copy a data set/partitioned data set to another data set/partitioned data set.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-files copy data-set [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n fromDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy from\\\\n\\\\n toDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy to\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --replace | --rep (boolean)\\\\n\\\\n Specify this option as true if you wish to replace like-named members in the\\\\n target data set\\\\n\\\\n --safe-replace | --safe-rep | --sr (boolean)\\\\n\\\\n Specify this option as true if you wish to replace like-named members or the\\\\n content of the target data set. This option will prompt to confirm.\\\\n\\\\n --response-timeout | --rto (number)\\\\n\\\\n The maximum amount of time in seconds the z/OSMF Files TSO servlet should run\\\\n before returning a response. Any request exceeding this amount of time will be\\\\n terminated and return an error. Allowed values: 5 - 600\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET' and replace like-named members:\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --replace\\\\n\\\\n - Copy the partitioned data set named 'TEST.PDS1' to the\\\\n partitioned data set named 'TEST.PDS2':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --safe-replace\\\\n\\\\n\\", + \\"stdout\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n data-set | ds\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Copy a data set/partitioned data set to another data set/partitioned data set.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-files copy data-set [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n fromDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy from\\\\n\\\\n toDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy to\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --replace | --rep (boolean)\\\\n\\\\n Specify this option as true if you wish to replace members with identical names\\\\n in the target data set\\\\n\\\\n --safe-replace | --safe-rep | --sr (boolean)\\\\n\\\\n Specify this option as true if you wish to replace members with identical names\\\\n or the content of the target data set. This option will prompt to confirm.\\\\n\\\\n --response-timeout | --rto (number)\\\\n\\\\n The maximum amount of time in seconds the z/OSMF Files TSO servlet should run\\\\n before returning a response. Any request exceeding this amount of time will be\\\\n terminated and return an error. Allowed values: 5 - 600\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET' and replace members with identical names:\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --replace\\\\n\\\\n - Copy the partitioned data set named 'TEST.PDS1' to the\\\\n partitioned data set named 'TEST.PDS2':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --safe-replace\\\\n\\\\n\\", \\"stderr\\": \\"\\", - \\"data\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n data-set | ds\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Copy a data set/partitioned data set to another data set/partitioned data set.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-files copy data-set [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n fromDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy from\\\\n\\\\n toDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy to\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --replace | --rep (boolean)\\\\n\\\\n Specify this option as true if you wish to replace like-named members in the\\\\n target data set\\\\n\\\\n --safe-replace | --safe-rep | --sr (boolean)\\\\n\\\\n Specify this option as true if you wish to replace like-named members or the\\\\n content of the target data set. This option will prompt to confirm.\\\\n\\\\n --response-timeout | --rto (number)\\\\n\\\\n The maximum amount of time in seconds the z/OSMF Files TSO servlet should run\\\\n before returning a response. Any request exceeding this amount of time will be\\\\n terminated and return an error. Allowed values: 5 - 600\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET' and replace like-named members:\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --replace\\\\n\\\\n - Copy the partitioned data set named 'TEST.PDS1' to the\\\\n partitioned data set named 'TEST.PDS2':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --safe-replace\\\\n\\\\n\\" + \\"data\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n data-set | ds\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Copy a data set/partitioned data set to another data set/partitioned data set.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-files copy data-set [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n fromDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy from\\\\n\\\\n toDataSetName\\\\t\\\\t (string)\\\\n\\\\n The name of the data set that you want to copy to\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --replace | --rep (boolean)\\\\n\\\\n Specify this option as true if you wish to replace members with identical names\\\\n in the target data set\\\\n\\\\n --safe-replace | --safe-rep | --sr (boolean)\\\\n\\\\n Specify this option as true if you wish to replace members with identical names\\\\n or the content of the target data set. This option will prompt to confirm.\\\\n\\\\n --response-timeout | --rto (number)\\\\n\\\\n The maximum amount of time in seconds the z/OSMF Files TSO servlet should run\\\\n before returning a response. Any request exceeding this amount of time will be\\\\n terminated and return an error. Allowed values: 5 - 600\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n member named 'USER.TO.SET(MEM2)':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET(mem2)\\\\\\"\\\\n\\\\n - Copy the data set member named 'USER.FROM.SET(MEM1)' to the\\\\n data set named 'USER.TO.SET':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET(mem1)\\\\\\" \\\\\\"USER.TO.SET\\\\\\"\\\\n\\\\n - Copy the data set named 'USER.FROM.SET' to the data set\\\\n named 'USER.TO.SET' and replace members with identical names:\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --replace\\\\n\\\\n - Copy the partitioned data set named 'TEST.PDS1' to the\\\\n partitioned data set named 'TEST.PDS2':\\\\n\\\\n $ zowe zos-files copy data-set \\\\\\"USER.FROM.SET\\\\\\" \\\\\\"USER.TO.SET\\\\\\" --safe-replace\\\\n\\\\n\\" }" `; diff --git a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts index a29a4bd79..0b88dadaf 100644 --- a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts +++ b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts @@ -273,7 +273,7 @@ describe("DsHandler", () => { const result = await promptForDuplicates(); expect(promptMock).toHaveBeenCalledWith( - `The source and target data sets have like named member names. The contents of those members will be overwritten.` + + `The source and target data sets have identical member names. The contents of those members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(true); @@ -309,7 +309,7 @@ describe("DsHandler", () => { const result = await promptForDuplicates(); expect(promptMock).toHaveBeenCalledWith( - `The source and target data sets have like named member names. The contents of those members will be overwritten.` + + `The source and target data sets have identical member names. The contents of those members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(false); diff --git a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/__snapshots__/Ds.definition.unit.test.ts.snap b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/__snapshots__/Ds.definition.unit.test.ts.snap index cb2c3439b..cae8422c3 100644 --- a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/__snapshots__/Ds.definition.unit.test.ts.snap +++ b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/__snapshots__/Ds.definition.unit.test.ts.snap @@ -6,7 +6,7 @@ Array [ "aliases": Array [ "rep", ], - "description": "Specify this option as true if you wish to replace like-named members in the target data set", + "description": "Specify this option as true if you wish to replace members with identical names in the target data set", "name": "replace", "type": "boolean", }, @@ -15,7 +15,7 @@ Array [ "safe-rep", "sr", ], - "description": "Specify this option as true if you wish to replace like-named members or the content of the target data set. This option will prompt to confirm.", + "description": "Specify this option as true if you wish to replace members with identical names or the content of the target data set. This option will prompt to confirm.", "name": "safe-replace", "type": "boolean", }, @@ -41,7 +41,7 @@ Array [ "options": "\\"USER.FROM.SET(mem1)\\" \\"USER.TO.SET\\"", }, Object { - "description": "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET' and replace like-named members", + "description": "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET' and replace members with identical names", "options": "\\"USER.FROM.SET\\" \\"USER.TO.SET\\" --replace", }, Object { diff --git a/packages/cli/src/zosfiles/-strings-/en.ts b/packages/cli/src/zosfiles/-strings-/en.ts index 8649f8866..405f913c3 100644 --- a/packages/cli/src/zosfiles/-strings-/en.ts +++ b/packages/cli/src/zosfiles/-strings-/en.ts @@ -195,8 +195,8 @@ export default { TODSNAME: "The name of the data set that you want to copy to" }, OPTIONS: { - REPLACE: "Specify this option as true if you wish to replace like-named members in the target data set", - SAFE_REPLACE: "Specify this option as true if you wish to replace like-named members or the content of the target data set. " + + REPLACE: "Specify this option as true if you wish to replace members with identical names in the target data set", + SAFE_REPLACE: "Specify this option as true if you wish to replace members with identical names or the content of the target data set. " + "This option will prompt to confirm." }, EXAMPLES: { @@ -204,7 +204,7 @@ export default { EX2: "Copy the data set member named 'USER.FROM.SET(MEM1)' to the data set member named 'USER.TO.SET(MEM2)'", EX3: "Copy the data set named 'USER.FROM.SET' to the data set member named 'USER.TO.SET(MEM2)'", EX4: "Copy the data set member named 'USER.FROM.SET(MEM1)' to the data set named 'USER.TO.SET'", - EX5: "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET' and replace like-named members", + EX5: "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET' and replace members with identical names", EX6: "Copy the partitioned data set named 'TEST.PDS1' to the partitioned data set named 'TEST.PDS2'", EX7: "Copy the partitioned data set named 'EXISTING.PDS' to a non-existent target 'NEW.PDS'" } diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts index 2f1666839..3a9a2bfa9 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts @@ -46,7 +46,7 @@ export default class DsHandler extends ZosFilesBaseHandler { private promptForLikeNamedMembers(console: IHandlerResponseConsoleApi) { return async() => { const answer: string = await console.prompt ( - `The source and target data sets have like named member names. The contents of those members will be overwritten.` + + `The source and target data sets have identical member names. The contents of those members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ) return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 609287225..e2e496a97 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes -- Enhancement: When copying PDSs with like-named members, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- Enhancement: When copying PDSs that have identical member names, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index ca3a78cae..9b1bee5d2 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -598,7 +598,7 @@ describe("Copy", () => { }); }); - describe("hasLikeNamedMembers", () => { + describe("hasIdenticalMemberNames", () => { beforeEach(async () => { try { await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_PARTITIONED, fromDataSetName); @@ -618,16 +618,16 @@ describe("Copy", () => { Imperative.console.info(`Error: ${inspect(err)}`); } }); - it("should return true if the source and target data sets have like-named members", async () => { - const response = await Copy["hasLikeNamedMembers"](REAL_SESSION, fromDataSetName, toDataSetName); + it("should return true if the source and target data sets have identical member names", async () => { + const response = await Copy["hasIdenticalMemberNames"](REAL_SESSION, fromDataSetName, toDataSetName); expect(response).toBe(true); }); - it("should return false if the source and target data sets do not have like-named members", async () => { + it("should return false if the source and target data sets do not have identical member names", async () => { await Delete.dataSet(REAL_SESSION, toDataSetName); await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_PARTITIONED, toDataSetName); - const response = await Copy["hasLikeNamedMembers"](REAL_SESSION, fromDataSetName, toDataSetName); + const response = await Copy["hasIdenticalMemberNames"](REAL_SESSION, fromDataSetName, toDataSetName); expect(response).toBe(false); }); }); diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index dc16779c4..ec1f5ecb5 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -35,7 +35,7 @@ describe("Copy", () => { const toDataSetName = "USER.DATA.TO"; const toMemberName = "mem2"; const isPDSSpy = jest.spyOn(Copy as any, "isPDS"); - const hasLikeNamedMembers = jest.spyOn(Copy as any, "hasLikeNamedMembers"); + const hasIdenticalMemberNames = jest.spyOn(Copy as any, "hasIdenticalMemberNames"); let dataSetExistsSpy: jest.SpyInstance; const promptFn = jest.fn(); const promptForLikeNamedMembers = jest.fn(); @@ -45,7 +45,7 @@ describe("Copy", () => { copyExpectStringSpy.mockClear().mockImplementation(async () => { return ""; }); isPDSSpy.mockClear().mockResolvedValue(false); dataSetExistsSpy = jest.spyOn(Copy as any, "dataSetExists").mockResolvedValue(true); - hasLikeNamedMembers.mockClear().mockResolvedValue(false); + hasIdenticalMemberNames.mockClear().mockResolvedValue(false); promptForLikeNamedMembers.mockClear(); }); afterAll(() => { @@ -622,8 +622,9 @@ describe("Copy", () => { commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message }); }); - it("should display a prompt for like named members if there are duplicate member names and --safe-replace and --replace flags are not used", async () => { - hasLikeNamedMembers.mockResolvedValue(true); + it("should display a prompt for identical member names if there are identical member names and" + + "--safe-replace and --replace flags are not used", async () => { + hasIdenticalMemberNames.mockResolvedValue(true); promptForLikeNamedMembers.mockClear().mockResolvedValue(true); const response = await Copy.dataSet( @@ -638,7 +639,7 @@ describe("Copy", () => { expect(response.success).toEqual(true); }); - it("should not display a prompt for like named members if there are no duplicate member names", async () => { + it("should not display a prompt for identical member names if there are no identical member names", async () => { const response = await Copy.dataSet( dummySession, { dsn: toDataSetName }, @@ -651,7 +652,7 @@ describe("Copy", () => { expect(promptForLikeNamedMembers).not.toHaveBeenCalled(); }); it("should throw error if user declines to replace the dataset", async () => { - hasLikeNamedMembers.mockResolvedValue(true); + hasIdenticalMemberNames.mockResolvedValue(true); promptForLikeNamedMembers.mockClear().mockResolvedValue(false); await expect(Copy.dataSet( @@ -768,10 +769,10 @@ describe("Copy", () => { const readStream = jest.spyOn(IO, "createReadStream"); const rmSync = jest.spyOn(fs, "rmSync"); const listDatasetSpy = jest.spyOn(List, "dataSet"); - const hasLikeNamedMembers = jest.spyOn(Copy as any, "hasLikeNamedMembers"); + const hasIdenticalMemberNames = jest.spyOn(Copy as any, "hasIdenticalMemberNames"); beforeEach(() => { - hasLikeNamedMembers.mockRestore(); + hasIdenticalMemberNames.mockRestore(); }); const dsPO = { @@ -901,13 +902,13 @@ describe("Copy", () => { }); }); - describe("hasLikeNamedMembers", () => { + describe("hasIdenticalMemberNames", () => { const listAllMembersSpy = jest.spyOn(List, "allMembers"); beforeEach(() => { jest.clearAllMocks(); }); - it("should return true if the source and target have like-named members", async () => { + it("should return true if the source and target have identical member names", async () => { listAllMembersSpy.mockImplementation(async (session, dsName): Promise => { if (dsName === fromDataSetName) { return { @@ -927,12 +928,12 @@ describe("Copy", () => { } }); - const response = await Copy["hasLikeNamedMembers"](dummySession, fromDataSetName, toDataSetName); + const response = await Copy["hasIdenticalMemberNames"](dummySession, fromDataSetName, toDataSetName); expect(response).toBe(true); expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, fromDataSetName); expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, toDataSetName); }); - it("should return false if the source and target do not have like-named members", async () => { + it("should return false if the source and target do not have identcal member names", async () => { const sourceResponse = { apiResponse: { items: [ @@ -956,7 +957,7 @@ describe("Copy", () => { } }); - const response = await Copy["hasLikeNamedMembers"](dummySession, fromDataSetName, toDataSetName); + const response = await Copy["hasIdenticalMemberNames"](dummySession, fromDataSetName, toDataSetName); expect(response).toBe(false); expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, fromDataSetName); diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 8b1c085d3..36c72ff9b 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -87,8 +87,8 @@ export class Copy { const targetIsPds = await this.isPDS(session, toDataSetName); if (sourceIsPds && targetIsPds) { - const hasLikeNamedMembers = await this.hasLikeNamedMembers(session, options["from-dataset"].dsn, toDataSetName); - if(!safeReplace && hasLikeNamedMembers && !overwriteMembers) { + const hasIdenticalMemberNames = await this.hasIdenticalMemberNames(session, options["from-dataset"].dsn, toDataSetName); + if(!safeReplace && hasIdenticalMemberNames && !overwriteMembers) { const userResponse = await options.promptForLikeNamedMembers(); if(!userResponse) { @@ -181,9 +181,9 @@ export class Copy { } /** - * Function that checks if source and target data sets have like-named members + * Function that checks if source and target data sets have identical member names */ - private static async hasLikeNamedMembers ( + private static async hasIdenticalMemberNames ( session: AbstractSession, fromPds: string, toPds: string From bc517800e7b5cdc72ce3bf53fe2e7c08f8cb321c Mon Sep 17 00:00:00 2001 From: Pujal Date: Wed, 5 Feb 2025 11:52:48 -0500 Subject: [PATCH 06/16] updated snapshots Signed-off-by: Pujal --- .../__snapshots__/ZosFilesUtils.unit.test.ts.snap | 3 +++ .../zosfiles/src/constants/ZosFiles.messages.ts | 7 +++++++ packages/zosfiles/src/methods/copy/Copy.ts | 13 ++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/zosfiles/__tests__/__unit__/utils/__snapshots__/ZosFilesUtils.unit.test.ts.snap b/packages/zosfiles/__tests__/__unit__/utils/__snapshots__/ZosFilesUtils.unit.test.ts.snap index 61acd4749..7c11fafd6 100644 --- a/packages/zosfiles/__tests__/__unit__/utils/__snapshots__/ZosFilesUtils.unit.test.ts.snap +++ b/packages/zosfiles/__tests__/__unit__/utils/__snapshots__/ZosFilesUtils.unit.test.ts.snap @@ -200,6 +200,9 @@ Destination: %s", "message": "Member(s) downloaded successfully. Destination: %s", }, + "membersContentTruncated": Object { + "message": "Member(s)' contents were truncated due to insufficient record lines. You can view the list of members here: %s", + }, "membersMatchedPattern": Object { "message": "%d members(s) were found matching pattern.", }, diff --git a/packages/zosfiles/src/constants/ZosFiles.messages.ts b/packages/zosfiles/src/constants/ZosFiles.messages.ts index f14887729..25761783b 100644 --- a/packages/zosfiles/src/constants/ZosFiles.messages.ts +++ b/packages/zosfiles/src/constants/ZosFiles.messages.ts @@ -197,6 +197,13 @@ export const ZosFilesMessages: { [key: string]: IMessageDefinition } = { message: "Member(s) downloaded successfully.\nDestination: %s" }, + /** + * Message indicating that the members contents were truncated due to lrecl + * @type {IMessageDefinition} + */ + membersContentTruncated: { + message: "Member(s)' contents were truncated due to insufficient record lines. You can view the list of members here: %s" + }, /** * Message indicating that the uss file was downloaded successfully * @type {IMessageDefinition} diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 36c72ff9b..5ebc99721 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -229,9 +229,16 @@ export class Copy { const uploadFileList: string[] = ZosFilesUtils.getFileListFromPath(downloadDir); for (const file of uploadFileList) { - const uploadingDsn = `${toPds}(${ZosFilesUtils.generateMemberName(file)})`; - const uploadStream = IO.createReadStream(file); - await Upload.streamToDataSet(session, uploadStream, uploadingDsn); + const memName = ZosFilesUtils.generateMemberName(file); + const uploadingDsn = `${toPds}(${memName})`; + try { + const uploadStream = IO.createReadStream(file); + await Upload.streamToDataSet(session, uploadStream, uploadingDsn); + } + catch(error) { + Logger.getAppLogger().error(error); + throw error; + } } fs.rmSync(downloadDir, {recursive: true}); return { From 059e3054cf1d0d9e4290cb76772a7890ca47f9ee Mon Sep 17 00:00:00 2001 From: Pujal Date: Wed, 5 Feb 2025 12:10:57 -0500 Subject: [PATCH 07/16] updates to code Signed-off-by: Pujal --- packages/zosfiles/src/methods/copy/Copy.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 5ebc99721..85ba90485 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -231,14 +231,8 @@ export class Copy { for (const file of uploadFileList) { const memName = ZosFilesUtils.generateMemberName(file); const uploadingDsn = `${toPds}(${memName})`; - try { - const uploadStream = IO.createReadStream(file); - await Upload.streamToDataSet(session, uploadStream, uploadingDsn); - } - catch(error) { - Logger.getAppLogger().error(error); - throw error; - } + const uploadStream = IO.createReadStream(file); + await Upload.streamToDataSet(session, uploadStream, uploadingDsn); } fs.rmSync(downloadDir, {recursive: true}); return { From 7c605b6cf9b71f6df3a7c5c3797d07fa4bc54237 Mon Sep 17 00:00:00 2001 From: Pujal Date: Thu, 6 Feb 2025 15:15:48 -0500 Subject: [PATCH 08/16] updates to changelogs Signed-off-by: Pujal --- packages/cli/CHANGELOG.md | 2 +- .../zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts | 8 ++++---- packages/cli/src/zosfiles/copy/ds/Ds.handler.ts | 4 ++-- packages/zosfiles/CHANGELOG.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 516428ff6..190c03041 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe CLI package will be documented in this file. ## Recent Changes -- Enhancement: When copying PDSs that have identical member names, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- Enhancement: When copying partitioned data sets that have identical member names, the user is now prompted to confirm before the operation occurs in case of potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added `--recordRange` flag to `zowe jobs download output` command to allow users to select a specific range of records to output from a spool file. [#2411](https://github.com/zowe/zowe-cli/pull/2411) - BugFix: The `zowe zos-files copy data-set` command overwrites the contents of the target data set without user confirmation. A `--safe-replace` option was added which prompts the user to confirm before overwriting the contents of the target data set. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts index 0b88dadaf..5ec6cf557 100644 --- a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts +++ b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts @@ -201,7 +201,7 @@ describe("DsHandler", () => { const result = await promptFn(commandParameters.arguments.toDataSetName); expect(promptMock).toHaveBeenCalledWith( - `The dataset '${toDataSetName}' exists on the target system. This copy may result in data loss.` + + `The dataset '${toDataSetName}' exists on the target system. This copy can result in data loss.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(true); @@ -237,7 +237,7 @@ describe("DsHandler", () => { const result = await promptFn(commandParameters.arguments.toDataSetName); expect(promptMock).toHaveBeenCalledWith( - `The dataset '${toDataSetName}' exists on the target system. This copy may result in data loss.` + + `The dataset '${toDataSetName}' exists on the target system. This copy can result in data loss.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(false); @@ -273,7 +273,7 @@ describe("DsHandler", () => { const result = await promptForDuplicates(); expect(promptMock).toHaveBeenCalledWith( - `The source and target data sets have identical member names. The contents of those members will be overwritten.` + + `The source and target data sets have identical member names. The contents of the target members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(true); @@ -309,7 +309,7 @@ describe("DsHandler", () => { const result = await promptForDuplicates(); expect(promptMock).toHaveBeenCalledWith( - `The source and target data sets have identical member names. The contents of those members will be overwritten.` + + `The source and target data sets have identical member names. The contents of the target members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ); expect(result).toBe(false); diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts index 3a9a2bfa9..34d635ad1 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts @@ -36,7 +36,7 @@ export default class DsHandler extends ZosFilesBaseHandler { private promptForSafeReplace(console: IHandlerResponseConsoleApi) { return async (targetDSN: string) => { const answer: string = await console.prompt( - `The dataset '${targetDSN}' exists on the target system. This copy may result in data loss.` + + `The dataset '${targetDSN}' exists on the target system. This copy can result in data loss.` + ` Are you sure you want to continue? [y/N]: ` ); return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); @@ -46,7 +46,7 @@ export default class DsHandler extends ZosFilesBaseHandler { private promptForLikeNamedMembers(console: IHandlerResponseConsoleApi) { return async() => { const answer: string = await console.prompt ( - `The source and target data sets have identical member names. The contents of those members will be overwritten.` + + `The source and target data sets have identical member names. The contents of the target members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` ) return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index e2e496a97..4523d9298 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes -- Enhancement: When copying PDSs that have identical member names, the user is now prompted to confirm before the operation occurs in case of data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- Enhancement: When copying PDSs that have identical member names, the user is now prompted to confirm before the operation occurs in case of potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) From 653b1385bc52018ef891ffd7d2c42a08d462d1b9 Mon Sep 17 00:00:00 2001 From: Pujal Date: Thu, 6 Feb 2025 15:23:38 -0500 Subject: [PATCH 09/16] updated likeNamed to Identical Signed-off-by: Pujal --- .../__unit__/copy/ds/Ds.handler.unit.test.ts | 14 ++++++------- .../cli/src/zosfiles/copy/ds/Ds.handler.ts | 4 ++-- .../__unit__/methods/copy/Copy.unit.test.ts | 20 +++++++++---------- packages/zosfiles/src/methods/copy/Copy.ts | 2 +- .../methods/copy/doc/ICopyDatasetOptions.ts | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts index 5ec6cf557..a847dff06 100644 --- a/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts +++ b/packages/cli/__tests__/zosfiles/__unit__/copy/ds/Ds.handler.unit.test.ts @@ -50,7 +50,7 @@ describe("DsHandler", () => { }, response: { - console: { promptFn: jest.fn(), promptForLikeNamedMembers: jest.fn() } + console: { promptFn: jest.fn(), promptForIdenticalNamedMembers: jest.fn() } } }; @@ -69,7 +69,7 @@ describe("DsHandler", () => { "responseTimeout": commandParameters.arguments.responseTimeout, "safeReplace": commandParameters.arguments.safeReplace, "promptFn": expect.any(Function), - "promptForLikeNamedMembers": expect.any(Function) + "promptForIdenticalNamedMembers": expect.any(Function) } ); expect(response).toBe(defaultReturn); @@ -99,7 +99,7 @@ describe("DsHandler", () => { responseTimeout }, response: { - console: { promptFn: jest.fn(), promptForLikeNamedMembers: jest.fn() } + console: { promptFn: jest.fn(), promptForIdenticalNamedMembers: jest.fn() } } }; @@ -118,7 +118,7 @@ describe("DsHandler", () => { "responseTimeout": commandParameters.arguments.responseTimeout, "safeReplace": commandParameters.arguments.safeReplace, "promptFn": expect.any(Function), - "promptForLikeNamedMembers": expect.any(Function) + "promptForIdenticalNamedMembers": expect.any(Function) } ); expect(response).toBe(defaultReturn); @@ -165,7 +165,7 @@ describe("DsHandler", () => { "responseTimeout": commandParameters.arguments.responseTimeout, "safeReplace": commandParameters.arguments.safeReplace, "promptFn": expect.any(Function), - "promptForLikeNamedMembers": expect.any(Function) + "promptForIdenticalNamedMembers": expect.any(Function) } ); expect(response).toBe(defaultReturn); @@ -269,7 +269,7 @@ describe("DsHandler", () => { const promptMock = jest.fn(); promptMock.mockResolvedValue("y"); - const promptForDuplicates = (handler as any)["promptForLikeNamedMembers"]({ prompt: promptMock }); + const promptForDuplicates = (handler as any)["promptForIdenticalNamedMembers"]({ prompt: promptMock }); const result = await promptForDuplicates(); expect(promptMock).toHaveBeenCalledWith( @@ -305,7 +305,7 @@ describe("DsHandler", () => { const promptMock = jest.fn(); promptMock.mockResolvedValue("N"); - const promptForDuplicates = (handler as any)["promptForLikeNamedMembers"]({ prompt: promptMock }); + const promptForDuplicates = (handler as any)["promptForIdenticalNamedMembers"]({ prompt: promptMock }); const result = await promptForDuplicates(); expect(promptMock).toHaveBeenCalledWith( diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts index 34d635ad1..7d91710e1 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts @@ -27,7 +27,7 @@ export default class DsHandler extends ZosFilesBaseHandler { responseTimeout: commandParameters.arguments.responseTimeout, safeReplace: commandParameters.arguments.safeReplace, promptFn: this.promptForSafeReplace(commandParameters.response.console), - promptForLikeNamedMembers: this.promptForLikeNamedMembers(commandParameters.response.console) + promptForIdenticalNamedMembers: this.promptForIdenticalNamedMembers(commandParameters.response.console) }; return Copy.dataSet(session, toDataSet, options); @@ -43,7 +43,7 @@ export default class DsHandler extends ZosFilesBaseHandler { }; } - private promptForLikeNamedMembers(console: IHandlerResponseConsoleApi) { + private promptForIdenticalNamedMembers(console: IHandlerResponseConsoleApi) { return async() => { const answer: string = await console.prompt ( `The source and target data sets have identical member names. The contents of the target members will be overwritten.` + diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index ec1f5ecb5..a549dd385 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -38,7 +38,7 @@ describe("Copy", () => { const hasIdenticalMemberNames = jest.spyOn(Copy as any, "hasIdenticalMemberNames"); let dataSetExistsSpy: jest.SpyInstance; const promptFn = jest.fn(); - const promptForLikeNamedMembers = jest.fn(); + const promptForIdenticalNamedMembers = jest.fn(); beforeEach(() => { copyPDSSpy.mockClear(); @@ -46,7 +46,7 @@ describe("Copy", () => { isPDSSpy.mockClear().mockResolvedValue(false); dataSetExistsSpy = jest.spyOn(Copy as any, "dataSetExists").mockResolvedValue(true); hasIdenticalMemberNames.mockClear().mockResolvedValue(false); - promptForLikeNamedMembers.mockClear(); + promptForIdenticalNamedMembers.mockClear(); }); afterAll(() => { isPDSSpy.mockRestore(); @@ -625,7 +625,7 @@ describe("Copy", () => { it("should display a prompt for identical member names if there are identical member names and" + "--safe-replace and --replace flags are not used", async () => { hasIdenticalMemberNames.mockResolvedValue(true); - promptForLikeNamedMembers.mockClear().mockResolvedValue(true); + promptForIdenticalNamedMembers.mockClear().mockResolvedValue(true); const response = await Copy.dataSet( dummySession, @@ -633,9 +633,9 @@ describe("Copy", () => { { "from-dataset": { dsn: fromDataSetName }, safeReplace: false, replace: false, - promptForLikeNamedMembers } + promptForIdenticalNamedMembers } ); - expect(promptForLikeNamedMembers).toHaveBeenCalledWith(); + expect(promptForIdenticalNamedMembers).toHaveBeenCalledWith(); expect(response.success).toEqual(true); }); @@ -646,14 +646,14 @@ describe("Copy", () => { { "from-dataset": { dsn: fromDataSetName }, safeReplace: false, replace: false, - promptForLikeNamedMembers } + promptForIdenticalNamedMembers } ); expect(response.success).toEqual(true); - expect(promptForLikeNamedMembers).not.toHaveBeenCalled(); + expect(promptForIdenticalNamedMembers).not.toHaveBeenCalled(); }); it("should throw error if user declines to replace the dataset", async () => { hasIdenticalMemberNames.mockResolvedValue(true); - promptForLikeNamedMembers.mockClear().mockResolvedValue(false); + promptForIdenticalNamedMembers.mockClear().mockResolvedValue(false); await expect(Copy.dataSet( dummySession, @@ -661,10 +661,10 @@ describe("Copy", () => { { "from-dataset": { dsn: fromDataSetName }, safeReplace: false, replace: false, - promptForLikeNamedMembers } + promptForIdenticalNamedMembers } )).rejects.toThrow(new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message })); - expect(promptForLikeNamedMembers).toHaveBeenCalled(); + expect(promptForIdenticalNamedMembers).toHaveBeenCalled(); }); }); it("should return early if the source and target data sets are identical", async () => { diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 85ba90485..3000c5913 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -89,7 +89,7 @@ export class Copy { if (sourceIsPds && targetIsPds) { const hasIdenticalMemberNames = await this.hasIdenticalMemberNames(session, options["from-dataset"].dsn, toDataSetName); if(!safeReplace && hasIdenticalMemberNames && !overwriteMembers) { - const userResponse = await options.promptForLikeNamedMembers(); + const userResponse = await options.promptForIdenticalNamedMembers(); if(!userResponse) { throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message}); diff --git a/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts b/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts index 948f69bfb..fabcc3659 100644 --- a/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts +++ b/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts @@ -52,5 +52,5 @@ export interface ICopyDatasetOptions extends IZosFilesOptions { * Prompt for duplicates * @returns True if target data set members */ - promptForLikeNamedMembers?: () => Promise; + promptForIdenticalNamedMembers?: () => Promise; } From 1786f7be6280cd553c685efa519cb749b8537d79 Mon Sep 17 00:00:00 2001 From: Pujal Date: Thu, 6 Feb 2025 15:26:23 -0500 Subject: [PATCH 10/16] updated changelog Signed-off-by: Pujal --- packages/zosfiles/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 4523d9298..9a83fc064 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes -- Enhancement: When copying PDSs that have identical member names, the user is now prompted to confirm before the operation occurs in case of potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- Enhancement: When copying partitioned data sets that have identical member names, the user is now prompted to confirm before the operation occurs in case of potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) From 1cb28c7ef558c74df5b7c37b4d4071c1f336fce3 Mon Sep 17 00:00:00 2001 From: Pujal Date: Fri, 7 Feb 2025 10:59:14 -0500 Subject: [PATCH 11/16] updated changelog to bugfix Signed-off-by: Pujal --- packages/cli/CHANGELOG.md | 2 +- packages/zosfiles/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 190c03041..0317c7aa0 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe CLI package will be documented in this file. ## Recent Changes -- Enhancement: When copying partitioned data sets that have identical member names, the user is now prompted to confirm before the operation occurs in case of potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- Enhancement: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added `--recordRange` flag to `zowe jobs download output` command to allow users to select a specific range of records to output from a spool file. [#2411](https://github.com/zowe/zowe-cli/pull/2411) - BugFix: The `zowe zos-files copy data-set` command overwrites the contents of the target data set without user confirmation. A `--safe-replace` option was added which prompts the user to confirm before overwriting the contents of the target data set. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 9a83fc064..ec322788a 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes -- Enhancement: When copying partitioned data sets that have identical member names, the user is now prompted to confirm before the operation occurs in case of potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) +- BugFix: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) From 5726d31353b9b8084c1305bbd8973d1ab8c6d0a4 Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 10 Feb 2025 09:39:31 -0500 Subject: [PATCH 12/16] updated changelog Signed-off-by: Pujal --- packages/cli/CHANGELOG.md | 9 +-------- packages/zosfiles/CHANGELOG.md | 3 --- .../__system__/methods/copy/Copy.system.test.ts | 5 +++-- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 036721663..be6967cd9 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -3,17 +3,10 @@ All notable changes to the Zowe CLI package will be documented in this file. ## `8.13.0` -<<<<<<< HEAD ## Recent Changes -<<<<<<< Updated upstream -- Enhancement: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) -======= -- Enhancement: Added the `--data-set-type` flag to create sequential data set command to allow for creating extended and large formatted sequential data sets. [#2141](https://github.com/zowe/zowe-cli/issues/2141) ->>>>>>> master -======= - BugFix: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) ->>>>>>> Stashed changes +- Enhancement: Added the `--data-set-type` flag to create sequential data set command to allow for creating extended and large formatted sequential data sets. [#2141](https://github.com/zowe/zowe-cli/issues/2141) - Enhancement: Added `--recordRange` flag to `zowe jobs download output` command to allow users to select a specific range of records to output from a spool file. [#2411](https://github.com/zowe/zowe-cli/pull/2411) - BugFix: The `zowe zos-files copy data-set` command overwrites the contents of the target data set without user confirmation. A `--safe-replace` option was added which prompts the user to confirm before overwriting the contents of the target data set. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index bb5786e95..d1eb45c06 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,11 +4,8 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## `8.13.0` -<<<<<<< HEAD - BugFix: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) -======= - BugFix: The `Create.dataSetValidateOptions()` function now correctly handles data set creation when the `dsorg` attribute is set to `PS-L` by automatically updating the `dsntype` attribute to `LARGE`. [#2141](https://github.com/zowe/zowe-cli/issues/2141) ->>>>>>> master - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index dd0fef368..209646601 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -11,7 +11,8 @@ import { Create, Upload, Delete, CreateDataSetTypeEnum, Copy, ZosFilesMessages, Get, IDataSet, ICrossLparCopyDatasetOptions, IGetOptions, IZosFilesResponse, - ZosFilesUtils} from "../../../../src"; + ZosFilesUtils, + List} from "../../../../src"; import { Imperative, IO, Session } from "@zowe/imperative"; import { inspect } from "util"; import { TestEnvironment } from "../../../../../../__tests__/__src__/environment/TestEnvironment"; @@ -22,7 +23,7 @@ import { ITestEnvironment } from "../../../../../../__tests__/__src__/environmen import { tmpdir } from "os"; import path = require("path"); import * as fs from "fs"; -import { ZosmfRestClient, List } from "@zowe/core-for-zowe-sdk"; +import { ZosmfRestClient } from "@zowe/core-for-zowe-sdk"; let REAL_SESSION: Session; From 5751f53d35b7b4c72bf42e938ff438e7586ada54 Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 10 Feb 2025 09:44:12 -0500 Subject: [PATCH 13/16] fixed changelog error Signed-off-by: Pujal --- packages/zosfiles/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index d1eb45c06..8187ea3cf 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -2,9 +2,11 @@ All notable changes to the Zowe z/OS files SDK package will be documented in this file. +## Recent Changes +- BugFix: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) + ## `8.13.0` -- BugFix: Users were not warned when copying partitioned data sets with identical member names. Now, the user is prompted to confirm before continuing the copy operation to avoid potential data loss. [#2349] (https://github.com/zowe/zowe-cli/issues/2349) - BugFix: The `Create.dataSetValidateOptions()` function now correctly handles data set creation when the `dsorg` attribute is set to `PS-L` by automatically updating the `dsntype` attribute to `LARGE`. [#2141](https://github.com/zowe/zowe-cli/issues/2141) - BugFix: Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - BugFix: The `Copy.data.set` function now prompts the user to confirm before overwriting the contents of the target data set with the addition of the `--safe-replace` option. [#2369] (https://github.com/zowe/zowe-cli/issues/2369) From 476f2a14e521ba7ea1f35d57bd818c40d8b2c6e2 Mon Sep 17 00:00:00 2001 From: Pujal Date: Mon, 10 Feb 2025 10:18:15 -0500 Subject: [PATCH 14/16] updated system tests Signed-off-by: Pujal --- packages/cli/src/zosfiles/-strings-/en.ts | 4 ++-- packages/cli/src/zosfiles/copy/ds/Ds.handler.ts | 2 +- .../__system__/methods/copy/Copy.system.test.ts | 10 +++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/zosfiles/-strings-/en.ts b/packages/cli/src/zosfiles/-strings-/en.ts index 15bef4afa..e9c567248 100644 --- a/packages/cli/src/zosfiles/-strings-/en.ts +++ b/packages/cli/src/zosfiles/-strings-/en.ts @@ -197,8 +197,8 @@ export default { }, OPTIONS: { REPLACE: "Specify this option as true if you wish to replace members with identical names in the target data set", - SAFE_REPLACE: "Specify this option as true if you wish to replace members with identical names or the content of the target data set. " + - "This option will prompt to confirm." + SAFE_REPLACE: "Specify this option as true if you wish to replace members with identical names or the content of " + + "the target data set. This option will prompt to confirm." }, EXAMPLES: { EX1: "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET'", diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts index 7d91710e1..0d899fec8 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts @@ -48,7 +48,7 @@ export default class DsHandler extends ZosFilesBaseHandler { const answer: string = await console.prompt ( `The source and target data sets have identical member names. The contents of the target members will be overwritten.` + ` Are you sure you want to continue? [y/N]: ` - ) + ); return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); }; } diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index 209646601..a7e128771 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -68,6 +68,14 @@ describe("Copy", () => { } }); describe("Success Scenarios", () => { + afterEach(async () => { + try { + await Delete.dataSet(REAL_SESSION, fromDataSetName); + await Delete.dataSet(REAL_SESSION, toDataSetName); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + }); describe("Sequential > Sequential", () => { beforeEach(async () => { try { @@ -109,7 +117,7 @@ describe("Copy", () => { expect(contents1.toString()).toEqual(contents2.toString()); }); }); - describe("Partioned > Partioned", () => { + describe("Partitioned > Partitioned", () => { let downloadDir: string; beforeEach(async () => { try { From 8063ce4129afc317b2d67ca9e3de64a19acb94d6 Mon Sep 17 00:00:00 2001 From: Pujal Date: Wed, 12 Feb 2025 14:29:25 -0500 Subject: [PATCH 15/16] list.allMembers call is cached Signed-off-by: Pujal --- packages/cli/src/zosfiles/-strings-/en.ts | 4 +- .../methods/copy/Copy.system.test.ts | 60 +++++++++++++++---- .../__unit__/methods/copy/Copy.unit.test.ts | 56 ++++++++++------- packages/zosfiles/src/methods/copy/Copy.ts | 15 +++-- 4 files changed, 90 insertions(+), 45 deletions(-) diff --git a/packages/cli/src/zosfiles/-strings-/en.ts b/packages/cli/src/zosfiles/-strings-/en.ts index e9c567248..5697f4487 100644 --- a/packages/cli/src/zosfiles/-strings-/en.ts +++ b/packages/cli/src/zosfiles/-strings-/en.ts @@ -197,8 +197,8 @@ export default { }, OPTIONS: { REPLACE: "Specify this option as true if you wish to replace members with identical names in the target data set", - SAFE_REPLACE: "Specify this option as true if you wish to replace members with identical names or the content of " + - "the target data set. This option will prompt to confirm." + SAFE_REPLACE: "Specify this option as true if you wish to replace members with identical names or the " + + "content of the target data set. This option will prompt to confirm." }, EXAMPLES: { EX1: "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET'", diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index a7e128771..ab62fbbb6 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -10,9 +10,7 @@ */ import { Create, Upload, Delete, CreateDataSetTypeEnum, Copy, ZosFilesMessages, Get, IDataSet, - ICrossLparCopyDatasetOptions, IGetOptions, IZosFilesResponse, - ZosFilesUtils, - List} from "../../../../src"; + ICrossLparCopyDatasetOptions, IGetOptions, IZosFilesResponse} from "../../../../src"; import { Imperative, IO, Session } from "@zowe/imperative"; import { inspect } from "util"; import { TestEnvironment } from "../../../../../../__tests__/__src__/environment/TestEnvironment"; @@ -20,12 +18,13 @@ import { ITestPropertiesSchema } from "../../../../../../__tests__/__src__/prope import { join } from "path"; import { readFileSync } from "fs"; import { ITestEnvironment } from "../../../../../../__tests__/__src__/environment/ITestEnvironment"; -import { tmpdir } from "os"; import path = require("path"); import * as fs from "fs"; +import { List, ZosFilesUtils } from "@zowe/zos-files-for-zowe-sdk"; +import * as util from "util"; +import { tmpdir } from "os"; import { ZosmfRestClient } from "@zowe/core-for-zowe-sdk"; - let REAL_SESSION: Session; let REAL_TARGET_SESSION: Session; let testEnvironment: ITestEnvironment; @@ -82,6 +81,7 @@ describe("Copy", () => { await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetName); await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, toDataSetName); await Upload.bufferToDataSet(REAL_SESSION, Buffer.from("1234"), fromDataSetName); + // await Upload.bufferToDataSet(REAL_SESSION, Buffer.from("abc"), fromDataSetName); } catch (err) { Imperative.console.info(`Error: ${inspect(err)}`); } @@ -162,13 +162,42 @@ describe("Copy", () => { expect(response).toBeTruthy(); expect(response.success).toBe(true); expect(response.commandResponse).toContain(ZosFilesMessages.datasetCopiedSuccessfully.message); - - expect(contents1).toBeTruthy(); - expect(contents2).toBeTruthy(); - expect(contents1.toString()).toEqual(contents2.toString()); }); - afterEach(() => { - fs.rmSync(downloadDir, { recursive: true, force: true }); + it("Should handle truncation errors and log them to a file", async () => { + let error; + let response; + + const uploadFileToDatasetSpy = jest.spyOn(Upload, 'fileToDataset').mockImplementation(async (session, filePath, dsn) => { + if (filePath === fileLocation) { + throw new Error("Truncation of a record occurred during an I/O operation"); + } + return Promise.resolve() as any; + }); + const copyDataSetSpy = jest.spyOn(Copy, 'dataSet').mockImplementation(async () => { + return { + success: true, + commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message + " " + + util.format(ZosFilesMessages.membersContentTruncated.message) + }; + }); + try { + response = await Copy.dataSet( + REAL_SESSION, + {dsn: toDataSetName}, + {"from-dataset": { + dsn:fromDataSetName + }} + ); + } catch (err) { + error = err; + Imperative.console.info(`Error: ${inspect(err)}`); + } + expect(response).toBeTruthy(); + expect(response.success).toBe(true); + expect(response.commandResponse).toContain(ZosFilesMessages.datasetCopiedSuccessfully.message + " " + + util.format(ZosFilesMessages.membersContentTruncated.message)); + uploadFileToDatasetSpy.mockRestore(); + copyDataSetSpy.mockRestore(); }); }); describe("Member > Member", () => { @@ -629,7 +658,9 @@ describe("Copy", () => { } }); it("should return true if the source and target data sets have identical member names", async () => { - const response = await Copy["hasIdenticalMemberNames"](REAL_SESSION, fromDataSetName, toDataSetName); + const sourceResponse = await List.allMembers(REAL_SESSION, fromDataSetName); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); + const response = await Copy["hasIdenticalMemberNames"](REAL_SESSION, sourceMemberList, toDataSetName); expect(response).toBe(true); }); @@ -637,7 +668,10 @@ describe("Copy", () => { await Delete.dataSet(REAL_SESSION, toDataSetName); await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_PARTITIONED, toDataSetName); - const response = await Copy["hasIdenticalMemberNames"](REAL_SESSION, fromDataSetName, toDataSetName); + const sourceResponse = await List.allMembers(REAL_SESSION, fromDataSetName); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); + + const response = await Copy["hasIdenticalMemberNames"](REAL_SESSION, sourceMemberList, toDataSetName); expect(response).toBe(false); }); }); diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index a549dd385..b1797afc6 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -39,6 +39,7 @@ describe("Copy", () => { let dataSetExistsSpy: jest.SpyInstance; const promptFn = jest.fn(); const promptForIdenticalNamedMembers = jest.fn(); + const listAllMembersSpy = jest.spyOn(List, "allMembers"); beforeEach(() => { copyPDSSpy.mockClear(); @@ -515,6 +516,14 @@ describe("Copy", () => { describe("Partitioned > Partitioned", () => { let createSpy: jest.SpyInstance; let dataSetExistsSpy: jest.SpyInstance; + const sourceResponse = { + apiResponse: { + items: [ + { member: "mem1" }, + { member: "mem2" }, + ] + } + }; beforeEach(() => { isPDSSpy.mockClear().mockResolvedValue(true); copyPDSSpy.mockClear().mockResolvedValue({success: true, commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message}); @@ -523,6 +532,7 @@ describe("Copy", () => { commandResponse: ZosFilesMessages.dataSetCreatedSuccessfully.message }); dataSetExistsSpy = jest.spyOn(Copy as any, "dataSetExists"); + listAllMembersSpy.mockImplementation(async (): Promise => sourceResponse); }); afterAll(() => { copyPDSSpy.mockRestore(); @@ -539,12 +549,11 @@ describe("Copy", () => { dsn:fromDataSetName }} ); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); expect(isPDSSpy).toHaveBeenNthCalledWith(1, dummySession, fromDataSetName); expect(isPDSSpy).toHaveBeenNthCalledWith(2, dummySession, toDataSetName); expect(copyPDSSpy).toHaveBeenCalledTimes(1); - expect(copyPDSSpy).toHaveBeenCalledWith(dummySession, fromDataSetName, toDataSetName); - expect(response).toEqual({ success: true, commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message @@ -875,6 +884,7 @@ describe("Copy", () => { ] } }; + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); const fileList = ["mem1", "mem2"]; listAllMembersSpy.mockImplementation(async (): Promise => sourceResponse); downloadAllMembersSpy.mockImplementation(async (): Promise => undefined); @@ -886,7 +896,7 @@ describe("Copy", () => { try{ - response = await Copy.copyPDS(dummySession, fromDataSetName, toDataSetName); + response = await Copy.copyPDS(dummySession, sourceMemberList, fromDataSetName, toDataSetName); } catch(e) { caughtError = e; @@ -909,28 +919,31 @@ describe("Copy", () => { jest.clearAllMocks(); }); it("should return true if the source and target have identical member names", async () => { + const sourceResponse = { + apiResponse: { + items: [ + { member: "mem1" }, + { member: "mem2" }, + ] + } + }; + const targetResponse = { + apiResponse: { + items: [ + { member: "mem1" }, + ] + } + }; listAllMembersSpy.mockImplementation(async (session, dsName): Promise => { if (dsName === fromDataSetName) { - return { - apiResponse: { - items: [ - { member: "mem1" }, - { member: "mem2" }, - ] - } - }; + return sourceResponse; } else if (dsName === toDataSetName) { - return { - apiResponse: { - items: [{ member: "mem1" }] - } - }; + return targetResponse; } }); - - const response = await Copy["hasIdenticalMemberNames"](dummySession, fromDataSetName, toDataSetName); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); + const response = await Copy["hasIdenticalMemberNames"](dummySession, sourceMemberList, toDataSetName); expect(response).toBe(true); - expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, fromDataSetName); expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, toDataSetName); }); it("should return false if the source and target do not have identcal member names", async () => { @@ -956,11 +969,10 @@ describe("Copy", () => { return targetResponse; } }); - - const response = await Copy["hasIdenticalMemberNames"](dummySession, fromDataSetName, toDataSetName); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); + const response = await Copy["hasIdenticalMemberNames"](dummySession, sourceMemberList, toDataSetName); expect(response).toBe(false); - expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, fromDataSetName); expect(listAllMembersSpy).toHaveBeenCalledWith(dummySession, toDataSetName); }); }); diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 3000c5913..1b10e8917 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -87,7 +87,10 @@ export class Copy { const targetIsPds = await this.isPDS(session, toDataSetName); if (sourceIsPds && targetIsPds) { - const hasIdenticalMemberNames = await this.hasIdenticalMemberNames(session, options["from-dataset"].dsn, toDataSetName); + const sourceResponse = await List.allMembers(session, options["from-dataset"].dsn); + const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); + + const hasIdenticalMemberNames = await this.hasIdenticalMemberNames(session, sourceMemberList, toDataSetName); if(!safeReplace && hasIdenticalMemberNames && !overwriteMembers) { const userResponse = await options.promptForIdenticalNamedMembers(); @@ -95,7 +98,7 @@ export class Copy { throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message}); } } - const response = await this.copyPDS(session, options["from-dataset"].dsn, toDataSetName); + const response = await this.copyPDS(session, sourceMemberList, options["from-dataset"].dsn, toDataSetName); return { success: true, commandResponse: newDataSet @@ -185,11 +188,9 @@ export class Copy { */ private static async hasIdenticalMemberNames ( session: AbstractSession, - fromPds: string, + sourceMemberList: string[], toPds: string ): Promise { - const sourceResponse = await List.allMembers(session, fromPds); - const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); const targetResponse = await List.allMembers(session, toPds); const targetMemberList = targetResponse.apiResponse.items.map((item: { member: any; }) => item.member); @@ -212,10 +213,8 @@ export class Copy { */ public static async copyPDS ( - session: AbstractSession, fromPds: string, toPds: string): Promise { + session: AbstractSession, sourceMemberList: string[], fromPds: string, toPds: string): Promise { try { - const sourceResponse = await List.allMembers(session, fromPds); - const sourceMemberList: Array<{ member: string }> = sourceResponse.apiResponse.items; if(sourceMemberList.length == 0) { return { From eeeb75508bd48ca0eaa466af42e96472ae3d64d5 Mon Sep 17 00:00:00 2001 From: Pujal Date: Wed, 12 Feb 2025 14:42:03 -0500 Subject: [PATCH 16/16] removed unsused variable Signed-off-by: Pujal --- .../zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index b1797afc6..077c6ff80 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -549,7 +549,6 @@ describe("Copy", () => { dsn:fromDataSetName }} ); - const sourceMemberList = sourceResponse.apiResponse.items.map((item: { member: any; }) => item.member); expect(isPDSSpy).toHaveBeenNthCalledWith(1, dummySession, fromDataSetName); expect(isPDSSpy).toHaveBeenNthCalledWith(2, dummySession, toDataSetName);