diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index a663993384..8bb8e266d1 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,7 +1,13 @@ # Change Log All notable changes to the Zowe CLI package will be documented in this file. + +## Recent Changes + +- 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) + ## `8.12.0` + - Enhancement: The `zowe zos-files copy data-set` command no longer requires the target data set to be preallocated. [##2349] (https://github.com/zowe/zowe-cli/issues/2349) ## `8.10.4` 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 63dd437f15..c432eb3b15 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 @@ -37,6 +37,11 @@ exports[`Copy Data Set should display the help 1`] = ` Specify this option as true if you wish to replace like-named members 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. + --response-timeout | --rto (number) The maximum amount of time in seconds the z/OSMF Files TSO servlet should run @@ -161,6 +166,11 @@ exports[`Copy Data Set should display the help 1`] = ` $ zowe zos-files copy data-set \\"USER.FROM.SET\\" \\"USER.TO.SET\\" --replace + - Copy the partitioned data set named 'TEST.PDS1' to the + partitioned data set named 'TEST.PDS2': + + $ zowe zos-files copy data-set \\"USER.FROM.SET\\" \\"USER.TO.SET\\" --safe-replace + " `; @@ -170,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 --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\\", + \\"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\\", \\"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 --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\\" + \\"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\\" }" `; 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 778b440d72..2975ad8629 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 @@ -33,11 +33,24 @@ describe("DsHandler", () => { const fromDataSetName = "ABCD"; const toDataSetName = "EFGH"; + const enq = "SHR"; + const replace = true; + const safeReplace = true; + const responseTimeout: any = undefined; + const commandParameters: any = { arguments: { fromDataSetName, - toDataSetName + toDataSetName, + enq, + replace, + safeReplace, + responseTimeout + + }, + response: { + console: { promptFn: jest.fn() } } }; @@ -49,7 +62,14 @@ describe("DsHandler", () => { expect(copyDatasetSpy).toHaveBeenLastCalledWith( dummySession, { dsn: commandParameters.arguments.toDataSetName }, - { "from-dataset": { dsn: commandParameters.arguments.fromDataSetName } } + { + "from-dataset": { dsn: commandParameters.arguments.fromDataSetName }, + "enq": commandParameters.arguments.enq, + "replace": commandParameters.arguments.replace, + "responseTimeout": commandParameters.arguments.responseTimeout, + "safeReplace": commandParameters.arguments.safeReplace, + "promptFn": expect.any(Function) + } ); expect(response).toBe(defaultReturn); }); @@ -63,11 +83,22 @@ describe("DsHandler", () => { const fromMemberName = "mem1"; const toDataSetName = "EFGH"; const toMemberName = "mem2"; + const enq = "SHR"; + const replace = true; + const safeReplace = true; + const responseTimeout: any = undefined; const commandParameters: any = { arguments: { fromDataSetName: `${fromDataSetName}(${fromMemberName})`, - toDataSetName: `${toDataSetName}(${toMemberName})` + toDataSetName: `${toDataSetName}(${toMemberName})`, + enq, + replace, + safeReplace, + responseTimeout + }, + response: { + console: { promptFn: jest.fn() } } }; @@ -79,7 +110,14 @@ describe("DsHandler", () => { expect(copyDatasetSpy).toHaveBeenLastCalledWith( dummySession, { dsn: toDataSetName, member: toMemberName }, - { "from-dataset": { dsn: fromDataSetName, member: fromMemberName } } + { + "from-dataset": { dsn: fromDataSetName, member: fromMemberName }, + "enq": commandParameters.arguments.enq, + "replace": commandParameters.arguments.replace, + "responseTimeout": commandParameters.arguments.responseTimeout, + "safeReplace": commandParameters.arguments.safeReplace, + "promptFn": expect.any(Function) + } ); expect(response).toBe(defaultReturn); }); @@ -93,13 +131,20 @@ describe("DsHandler", () => { const toDataSetName = "EFGH"; const enq = "SHR"; const replace = true; + const safeReplace = true; + const responseTimeout: any = undefined; const commandParameters: any = { arguments: { fromDataSetName, toDataSetName, enq, - replace + replace, + safeReplace, + responseTimeout + }, + response: { + console: { promptFn: jest.fn() } } }; @@ -114,9 +159,84 @@ describe("DsHandler", () => { { "from-dataset": { dsn: commandParameters.arguments.fromDataSetName }, "enq": commandParameters.arguments.enq, - "replace": commandParameters.arguments.replace + "replace": commandParameters.arguments.replace, + "responseTimeout": commandParameters.arguments.responseTimeout, + "safeReplace": commandParameters.arguments.safeReplace, + "promptFn": expect.any(Function) } ); expect(response).toBe(defaultReturn); }); + it("should prompt the user 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 = true; + const safeReplace = true; + 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 promptFn = (handler as any)["promptForSafeReplace"]({ prompt: promptMock }); + 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.` + + ` Are you sure you want to continue? [y/N]: ` + ); + expect(result).toBe(true); + }); + it("should prompt the user and return true when input is 'N'", async () => { + const handler = new DsHandler(); + + expect(handler).toBeInstanceOf(ZosFilesBaseHandler); + const fromDataSetName = "ABCD"; + const toDataSetName = "EFGH"; + const enq = "SHR"; + const replace = true; + const safeReplace = true; + 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 promptFn = (handler as any)["promptForSafeReplace"]({ prompt: promptMock }); + 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.` + + ` 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 cee4e8287e..7e0c8a3fa9 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 @@ -10,6 +10,15 @@ Array [ "name": "replace", "type": "boolean", }, + Object { + "aliases": 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.", + "name": "safe-replace", + "type": "boolean", + }, ] `; @@ -35,5 +44,9 @@ Array [ "description": "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET' and replace like-named members", "options": "\\"USER.FROM.SET\\" \\"USER.TO.SET\\" --replace", }, + Object { + "description": "Copy the partitioned data set named 'TEST.PDS1' to the partitioned data set named 'TEST.PDS2'", + "options": "\\"USER.FROM.SET\\" \\"USER.TO.SET\\" --safe-replace", + }, ] `; diff --git a/packages/cli/src/zosfiles/-strings-/en.ts b/packages/cli/src/zosfiles/-strings-/en.ts index dad7b32bf6..8649f8866b 100644 --- a/packages/cli/src/zosfiles/-strings-/en.ts +++ b/packages/cli/src/zosfiles/-strings-/en.ts @@ -195,7 +195,9 @@ 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" + 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. " + + "This option will prompt to confirm." }, EXAMPLES: { EX1: "Copy the data set named 'USER.FROM.SET' to the data set named 'USER.TO.SET'", @@ -204,7 +206,7 @@ export default { 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", EX6: "Copy the partitioned data set named 'TEST.PDS1' to the partitioned data set named 'TEST.PDS2'", - EX7: "Copy the partionted data set named 'EXISTING.PDS' to a non-existent target 'NEW.PDS'" + EX7: "Copy the partitioned data set named 'EXISTING.PDS' to a non-existent target 'NEW.PDS'" } }, DATA_SET_CROSS_LPAR: { diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.definition.ts b/packages/cli/src/zosfiles/copy/ds/Ds.definition.ts index 6324f99352..a3d9cb4b28 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.definition.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.definition.ts @@ -53,6 +53,12 @@ export const DsDefinition: ICommandDefinition = { aliases: ["rep"], description: strings.OPTIONS.REPLACE, type: "boolean" + }, + { + name: "safe-replace", + aliases: ["safe-rep", "--sr"], + description: strings.OPTIONS.SAFE_REPLACE, + type: "boolean", } ] as ICommandOptionDefinition[]).sort((a, b) => a.name.localeCompare(b.name) @@ -77,6 +83,10 @@ export const DsDefinition: ICommandDefinition = { { description: strings.EXAMPLES.EX5, options: `"USER.FROM.SET" "USER.TO.SET" --replace` + }, + { + description: strings.EXAMPLES.EX6, + options: `"USER.FROM.SET" "USER.TO.SET" --safe-replace` } ] }; diff --git a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts index 47a21cfb62..40913a7d5c 100644 --- a/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts +++ b/packages/cli/src/zosfiles/copy/ds/Ds.handler.ts @@ -9,7 +9,7 @@ * */ -import { AbstractSession, IHandlerParameters } from "@zowe/imperative"; +import { AbstractSession, IHandlerParameters, IHandlerResponseConsoleApi } from "@zowe/imperative"; import { Copy, IZosFilesResponse, IDataSet, ICopyDatasetOptions, ZosFilesUtils } from "@zowe/zos-files-for-zowe-sdk"; import { ZosFilesBaseHandler } from "../../ZosFilesBase.handler"; @@ -24,9 +24,21 @@ export default class DsHandler extends ZosFilesBaseHandler { "from-dataset": fromDataSet, enq: commandParameters.arguments.enq, replace: commandParameters.arguments.replace, - responseTimeout: commandParameters.arguments.responseTimeout + responseTimeout: commandParameters.arguments.responseTimeout, + safeReplace: commandParameters.arguments.safeReplace, + promptFn: this.promptForSafeReplace(commandParameters.response.console) }; return Copy.dataSet(session, toDataSet, options); } + + 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.` + + ` Are you sure you want to continue? [y/N]: ` + ); + return answer != null && (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); + }; + } } diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index 059c83a266..6d269b63d1 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to the Imperative package will be documented in this file. +## Recent Changes +- Format fix: `DeferredPromise` and `DeferredPromise.unit.test` comment format changed to match standard. + ## `8.11.0` - Enhancement: Added `DeferredPromise` class to Imperative to provide utilities for data synchronization. [#2405](https://github.com/zowe/zowe-cli/pull/2405) diff --git a/packages/imperative/src/utilities/__tests__/DeferredPromise.unit.test.ts b/packages/imperative/src/utilities/__tests__/DeferredPromise.unit.test.ts index 146067c919..7ab1cf1bbe 100644 --- a/packages/imperative/src/utilities/__tests__/DeferredPromise.unit.test.ts +++ b/packages/imperative/src/utilities/__tests__/DeferredPromise.unit.test.ts @@ -1,13 +1,13 @@ -/** - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - * - */ +/* +* This program and the accompanying materials are made available under the terms of the +* Eclipse Public License v2.0 which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Copyright Contributors to the Zowe Project. +* +*/ import { DeferredPromise, DeferredPromiseStatus } from "../src/DeferredPromise"; diff --git a/packages/imperative/src/utilities/src/DeferredPromise.ts b/packages/imperative/src/utilities/src/DeferredPromise.ts index 20b8fcfd22..65324d0855 100644 --- a/packages/imperative/src/utilities/src/DeferredPromise.ts +++ b/packages/imperative/src/utilities/src/DeferredPromise.ts @@ -1,13 +1,13 @@ -/** - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - * - */ +/* +* This program and the accompanying materials are made available under the terms of the +* Eclipse Public License v2.0 which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Copyright Contributors to the Zowe Project. +* +*/ /* Status of the deferred promise */ export enum DeferredPromiseStatus { diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 186ef43342..2a6e4353a6 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,10 +4,12 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes -- 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: 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) ## `8.12.0` +## `8.12.0` - Enhancement: The `Copy.dataset` function now creates a new data set if the entered target data set does not exist. [#2349](https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added the `maxLength` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the maximum number of items to return. [#2409](https://github.com/zowe/zowe-cli/pull/2409) - Enhancement: Added the `start` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the first data set/member name to return in the response. [#2409](https://github.com/zowe/zowe-cli/pull/2409) 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 27652eea7b..859457672e 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -471,6 +471,87 @@ describe("Copy", () => { expect(contents1.toString()).toEqual(contents2.toString()); }); }); + + describe("Safe replace option", () => { + const promptFn = jest.fn(); + beforeEach(async () => { + try { + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetName); + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, toDataSetName); + await Upload.fileToDataset(REAL_SESSION, fileLocation, fromDataSetName); + await Copy.dataSet( + REAL_SESSION, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName } } + ); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + }); + + it("Should succeed with safe replace option", async () => { + let error; + let response; + let contents1; + let contents2; + promptFn.mockResolvedValue(true); + + try { + response = await Copy.dataSet( + REAL_SESSION, + { dsn: toDataSetName}, + { + "from-dataset": { dsn: fromDataSetName }, + "safeReplace": true, + promptFn + } + ); + contents1 = await Get.dataSet(REAL_SESSION, `${fromDataSetName}`); + contents2 = await Get.dataSet(REAL_SESSION, `${toDataSetName}`); + Imperative.console.info(`Response: ${inspect(response)}`); + } catch (err) { + error = err; + Imperative.console.info(`Error: ${inspect(err)}`); + } + + expect(error).toBeFalsy(); + expect(promptFn).toHaveBeenCalledWith(toDataSetName); + 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()); + }); + + it("Should result in error when safe replace option is selected but the user declines the prompt", async () => { + let error; + let response; + promptFn.mockResolvedValue(false); + + try { + response = await Copy.dataSet( + REAL_SESSION, + { dsn: toDataSetName, member: file2 }, + { + "from-dataset": { dsn: fromDataSetName}, + "safeReplace": true, + promptFn + } + ); + Imperative.console.info(`Response: ${inspect(response)}`); + } catch (err) { + error = err; + Imperative.console.info(`Error: ${inspect(err)}`); + } + + expect(error).toBeTruthy(); + expect(error.message).toContain(ZosFilesMessages.datasetCopiedAborted.message); + expect(response).toBeFalsy(); + }); + + }); describe("responseTimeout option", () => { beforeEach(async () => { try { @@ -637,7 +718,7 @@ describe("Copy", () => { } expect(response?.success).toBeFalsy(); expect(error).toBeDefined(); - expect(error.message).toContain("Data set copied aborted. The existing target data set was not overwritten."); + expect(error.message).toContain("Data set copy aborted. The existing target data set was not overwritten."); }); }); @@ -777,7 +858,7 @@ describe("Copy", () => { } expect(response?.success).toBeFalsy(); expect(error).toBeDefined(); - expect(error.message).toContain("Data set copied aborted. The existing target data set was not overwritten."); + expect(error.message).toContain("Data set copy aborted. The existing target data set was not overwritten."); }); }); @@ -891,7 +972,7 @@ describe("Copy", () => { } expect(response?.success).toBeFalsy(); expect(error).toBeDefined(); - expect(error.message).toContain("Data set copied aborted. The existing target data set was not overwritten."); + expect(error.message).toContain("Data set copy aborted. The existing target data set was not overwritten."); }); }); @@ -1074,7 +1155,7 @@ describe("Copy", () => { } expect(response?.success).toBeFalsy(); expect(error).toBeDefined(); - expect(error.message).toContain("Data set copied aborted. The existing target data set was not overwritten."); + expect(error.message).toContain("Data set copy aborted. The existing target data set was not 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 ea3ef20496..2ecad85e7a 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -36,6 +36,7 @@ describe("Copy", () => { const toMemberName = "mem2"; const isPDSSpy = jest.spyOn(Copy as any, "isPDS"); let dataSetExistsSpy: jest.SpyInstance; + const promptFn = jest.fn(); beforeEach(() => { copyPDSSpy.mockClear(); @@ -459,6 +460,55 @@ describe("Copy", () => { expect(lastArgumentOfCall).toHaveProperty("replace", false); }); }); + describe("Safe replace option", () => { + it("should not throw error if safeReplace has value of true", async () => { + promptFn.mockResolvedValue(true); + + const response = await Copy.dataSet( + dummySession, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName }, + safeReplace: true, + promptFn } + ); + + expect(copyExpectStringSpy).toHaveBeenCalledTimes(1); + const argumentsOfCall = copyExpectStringSpy.mock.calls[0]; + const lastArgumentOfCall = argumentsOfCall[argumentsOfCall.length - 1]; + expect(lastArgumentOfCall).toHaveProperty("safeReplace", true); + expect(response).toEqual({success: true, commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message}); + expect(promptFn).toHaveBeenCalledWith(toDataSetName); + }); + + it("should throw error if user declines to replace the dataset", async () => { + promptFn.mockResolvedValue(false); + + await expect(Copy.dataSet( + dummySession, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName }, + safeReplace: true, + promptFn } + )).rejects.toThrow(new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message })); + + expect(promptFn).toHaveBeenCalledWith(toDataSetName); + }); + + it("should not throw error if safeReplace has value of false", async () => { + await expect(Copy.dataSet( + dummySession, + { dsn: toDataSetName }, + { "from-dataset": { dsn: fromDataSetName }, + safeReplace: false, + } + )).resolves.not.toThrow(); + expect(copyExpectStringSpy).toHaveBeenCalledTimes(1); + const argumentsOfCall = copyExpectStringSpy.mock.calls[0]; + const lastArgumentOfCall = argumentsOfCall[argumentsOfCall.length - 1]; + expect(lastArgumentOfCall).toHaveProperty("safeReplace", false); + }); + + }); describe("Partitioned > Partitioned", () => { let createSpy: jest.SpyInstance; let dataSetExistsSpy: jest.SpyInstance; 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 59245f88cb..61acd4749f 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 @@ -85,7 +85,7 @@ Object { "message": "Data set allocation aborted. The \\"allocate like\\" data set was not found.", }, "datasetCopiedAborted": Object { - "message": "Data set copied aborted. The existing target data set was not overwritten.", + "message": "Data set copy aborted. The existing target data set was not overwritten.", }, "datasetCopiedAbortedNoPDS": Object { "message": "Data set copied aborted. Copying from a PDS to PDS is not supported when using the 'dsclp' option.", diff --git a/packages/zosfiles/src/constants/ZosFiles.messages.ts b/packages/zosfiles/src/constants/ZosFiles.messages.ts index 33d74079ec..9c4b9d8e7a 100644 --- a/packages/zosfiles/src/constants/ZosFiles.messages.ts +++ b/packages/zosfiles/src/constants/ZosFiles.messages.ts @@ -678,7 +678,7 @@ export const ZosFilesMessages: { [key: string]: IMessageDefinition } = { * @type {IMessageDefinition} */ datasetCopiedAborted: { - message: "Data set copied aborted. The existing target data set was not overwritten." + message: "Data set copy aborted. The existing target data set was not overwritten." }, /** diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 7e5d91d90e..2894cc5c45 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -57,6 +57,7 @@ export class Copy { ): Promise { ImperativeExpect.toBeDefinedAndNonBlank(options["from-dataset"].dsn, "fromDataSetName"); ImperativeExpect.toBeDefinedAndNonBlank(toDataSetName, "toDataSetName"); + const safeReplace: boolean = options.safeReplace; if(options["from-dataset"].dsn === toDataSetName && toMemberName === options["from-dataset"].member) { return { @@ -71,6 +72,15 @@ export class Copy { if (newDataSet) { await Create.dataSetLike(session, toDataSetName, options["from-dataset"].dsn); } + else if(safeReplace) { + if (options.promptFn != null) { + const userResponse = await options.promptFn(toDataSetName); + + if(!userResponse) { + throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAborted.message }); + } + } + } if(!toMemberName && !options["from-dataset"].member) { const sourceIsPds = await this.isPDS(session, options["from-dataset"].dsn); const targetIsPds = await this.isPDS(session, toDataSetName); diff --git a/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts b/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts index b0e2285db6..23d75b7967 100644 --- a/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts +++ b/packages/zosfiles/src/methods/copy/doc/ICopyDatasetOptions.ts @@ -34,4 +34,17 @@ export interface ICopyDatasetOptions extends IZosFilesOptions { * @type {boolean} */ replace?: boolean; + + /** + * Safe replace option + * @type {boolean}; + */ + safeReplace?: boolean; + + /** + * Prompt callback that will be invoked before overwiting a data set. + * @param targetDSN Name of data set that already exists + * @returns True if target data set should be overwritten + */ + promptFn?: (targetDSN: string) => Promise; } diff --git a/packages/zosfiles/src/methods/copy/doc/ICrossLparCopyDatasetOptions.ts b/packages/zosfiles/src/methods/copy/doc/ICrossLparCopyDatasetOptions.ts index 67eb680818..dc954fe028 100644 --- a/packages/zosfiles/src/methods/copy/doc/ICrossLparCopyDatasetOptions.ts +++ b/packages/zosfiles/src/methods/copy/doc/ICrossLparCopyDatasetOptions.ts @@ -44,11 +44,4 @@ export interface ICrossLparCopyDatasetOptions extends ICopyDatasetOptions { * @type {boolean} */ overwrite?: boolean; - - /** - * Prompt callback that will be invoked before overwiting a data set. - * @param targetDSN Name of data set that already exists - * @returns True if target data set should be overwritten - */ - promptFn?: (targetDSN: string) => Promise; }