Skip to content

Commit

Permalink
Merge pull request #2393 from zowe/copy-pds-enhancement
Browse files Browse the repository at this point in the history
copy pds into new target data set enhancement
  • Loading branch information
t1m0thyj authored Jan 15, 2025
2 parents 2a299fc + 75f0719 commit 97a3c68
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 19 deletions.
4 changes: 3 additions & 1 deletion packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Change Log
All notable changes to the Zowe CLI package will be documented in this file.

## `8.10.4`
## Recent Changes
- 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`
- BugFix: Fixed an issue where the `zowe files upload dir-to-uss` command was missing progress bar to track progress of file uploads. [#2344](https://github.com/zowe/zowe-cli/issues/2344)

## `8.10.3`
Expand Down

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions packages/cli/src/zosfiles/-strings-/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export default {
DESCRIPTION: "Copy a data set/partitioned data set to another data set/partitioned data set.",
POSITIONALS: {
FROMDSNAME: "The name of the data set that you want to copy from",
TODSNAME: "The name of the data set that you want to copy to (data set must be preallocated)"
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"
Expand All @@ -203,7 +203,8 @@ export default {
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",
EX6: "Copy the partitioned data set named 'TEST.PDS1' to the partitioned data set named 'TEST.PDS2'"
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'"
}
},
DATA_SET_CROSS_LPAR: {
Expand Down
5 changes: 4 additions & 1 deletion packages/zosfiles/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

All notable changes to the Zowe z/OS files SDK package will be documented in this file.

## `8.10.3`

## Recent Changes
- 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)

## `8.10.3`
- BugFix: The `Copy.dataset` method no longer copies all partitioned data set members if a member is passed to the function. [#2402](https://github.com/zowe/zowe-cli/pull/2402)

## `8.10.0`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,54 @@ describe("Copy", () => {
});
});
});
describe("dataSetsIdentical", () => {
beforeEach(async () => {
try {
await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetName);
await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetName);
} catch (err) {
Imperative.console.info(`Error: ${inspect(err)}`);
}
});
it("should return false when the source and target data sets are indentical", async () => {
let error;
let response;
try {
response = await Copy.dataSet(
REAL_SESSION,
{dsn: fromDataSetName},
{"from-dataset": {
dsn:fromDataSetName
}}
);
}
catch(err) {
error = err;
Imperative.console.info(`Error: ${inspect(err)}`);
}
expect(error).toBeFalsy();

expect(response).toBeTruthy();
expect(response.success).toBe(false);
expect(response.commandResponse).toContain(ZosFilesMessages.identicalDataSets.message);

});
});
describe("dataSetExists", () => {
it("should return true when the dataset exists", async () => {
try {
await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetName);
} catch (err) {
Imperative.console.info(`Error: ${inspect(err)}`);
}
const exists = await Copy["dataSetExists"](REAL_SESSION, fromDataSetName);
expect(exists).toBe(true);
});
it("should return false when the dataset does not exist", async () => {
const exists = await Copy["dataSetExists"](REAL_SESSION, fromDataSetName);
expect(exists).toBe(false);
});
});
describe("Enq option", () => {
beforeEach(async () => {
try {
Expand Down
102 changes: 100 additions & 2 deletions packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Session, ImperativeError, IO } from "@zowe/imperative";
import { posix } from "path";
import * as fs from "fs";
import { error } from "console";

import * as util from "util";
import { Copy, Create, Get, List, Upload, ZosFilesConstants, ZosFilesMessages, IZosFilesResponse, Download, ZosFilesUtils } from "../../../../src";
import { ZosmfHeaders, ZosmfRestClient } from "@zowe/core-for-zowe-sdk";

Expand All @@ -35,14 +35,18 @@ describe("Copy", () => {
const toDataSetName = "USER.DATA.TO";
const toMemberName = "mem2";
const isPDSSpy = jest.spyOn(Copy as any, "isPDS");
let dataSetExistsSpy: jest.SpyInstance;

beforeEach(() => {
copyPDSSpy.mockClear();
copyExpectStringSpy.mockClear().mockImplementation(async () => { return ""; });
isPDSSpy.mockClear().mockResolvedValue(false);
dataSetExistsSpy = jest.spyOn(Copy as any, "dataSetExists").mockResolvedValue(true);

});
afterAll(() => {
isPDSSpy.mockRestore();
dataSetExistsSpy.mockRestore();
});
describe("Success Scenarios", () => {
describe("Sequential > Sequential", () => {
Expand Down Expand Up @@ -456,13 +460,24 @@ describe("Copy", () => {
});
});
describe("Partitioned > Partitioned", () => {
let createSpy: jest.SpyInstance;
let dataSetExistsSpy: jest.SpyInstance;
beforeEach(() => {
isPDSSpy.mockClear().mockResolvedValue(true);
copyPDSSpy.mockClear().mockResolvedValue({success: true, commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message});
createSpy = jest.spyOn(Create, "dataSetLike").mockResolvedValue({
success: true,
commandResponse: ZosFilesMessages.dataSetCreatedSuccessfully.message
});
dataSetExistsSpy = jest.spyOn(Copy as any, "dataSetExists");
});
afterAll(() => {
copyPDSSpy.mockRestore();
});
afterEach(() => {
createSpy.mockRestore();
dataSetExistsSpy.mockRestore();
});
it("should call copyPDS to copy members of source PDS to target PDS", async () => {
const response = await Copy.dataSet(
dummySession,
Expand Down Expand Up @@ -524,6 +539,50 @@ describe("Copy", () => {
expectedPayload
);
});
it("should call Create.dataSetLike and create a new data set if the target data set inputted does not exist", async() => {
dataSetExistsSpy.mockResolvedValue(false);
const response = await Copy.dataSet(
dummySession,
{dsn: toDataSetName},
{"from-dataset": {
dsn:fromDataSetName
}}
);
expect(createSpy).toHaveBeenCalled();
expect(response).toEqual({
success: true,
commandResponse: util.format(ZosFilesMessages.dataSetCopiedIntoNew.message, toDataSetName)
});
});
it("should not create a new data set if the target data set inputted exists", async() => {
dataSetExistsSpy.mockResolvedValue(true);
const response = await Copy.dataSet(
dummySession,
{dsn: toDataSetName},
{"from-dataset": {
dsn:fromDataSetName
}}
);
expect(createSpy).not.toHaveBeenCalled();
expect(response).toEqual({
success: true,
commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message
});
});
});
it("should return early if the source and target data sets are identical", async () => {
const response = await Copy.dataSet(
dummySession,
{dsn: fromDataSetName},
{"from-dataset": {
dsn: fromDataSetName
}}
);

expect(response).toEqual({
success: false,
commandResponse: `The source and target data sets are identical.`
});
});
});
describe("Failure Scenarios", () => {
Expand Down Expand Up @@ -662,7 +721,47 @@ describe("Copy", () => {
expect(response).toEqual(false);
expect(listDatasetSpy).toHaveBeenCalledWith(dummySession, dsPS.dsname, { attributes: true });
});
it("should return true if the data set exists", async () => {
let caughtError;
let response;
listDatasetSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
returnedRows: 1,
items: [dsPO]
}
};
});
try {
response = await (Copy as any).dataSetExists(dummySession, dsPO.dsname);
}
catch(e) {
caughtError = e;
}
expect(response).toEqual(true);
expect(listDatasetSpy).toHaveBeenCalledWith(dummySession, dsPO.dsname, {start: dsPO.dsname});
});

it("should return false if the data set does not exist", async () => {
let caughtError;
let response;
listDatasetSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
returnedRows: 0,
items: []
}
};
});
try {
response = await (Copy as any).dataSetExists(dummySession, dsPO.dsname);
}
catch(e) {
caughtError = e;
}
expect(response).toEqual(false);
expect(listDatasetSpy).toHaveBeenCalledWith(dummySession, dsPO.dsname, {start: dsPO.dsname});
});
it("should successfully copy members from source to target PDS", async () => {
let caughtError;
let response;
Expand All @@ -675,7 +774,6 @@ describe("Copy", () => {
}
};
const fileList = ["mem1", "mem2"];

listAllMembersSpy.mockImplementation(async (): Promise<any> => sourceResponse);
downloadAllMembersSpy.mockImplementation(async (): Promise<any> => undefined);
fileListPathSpy.mockReturnValue(fileList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ Object {
"commonWithValue": Object {
"message": "with value",
},
"dataSetCopiedIntoNew": Object {
"message": "Source contents were successfully copied into a new data set - %s",
},
"dataSetCreatedSuccessfully": Object {
"message": "Data set created successfully.",
},
Expand Down Expand Up @@ -140,6 +143,9 @@ Destination: %s",
"fsUnmountedSuccessfully": Object {
"message": "File system unmounted successfully.",
},
"identicalDataSets": Object {
"message": "The source and target data sets are identical.",
},
"invalidAlcunitOption": Object {
"message": "Invalid zos-files create command 'alcunit' option: ",
},
Expand Down
16 changes: 15 additions & 1 deletion packages/zosfiles/src/constants/ZosFiles.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ export const ZosFilesMessages: { [key: string]: IMessageDefinition } = {
unsupportedDataType: {
message: "Unsupported data type 'record' specified for USS file operation."
},

/**
* Message indicating that the source and target data sets are identical
* @type {IMessageDefinition}
*/
identicalDataSets: {
message: "The source and target data sets are identical."
},
/**
* Message indicating that the data set type is required
* @type {IMessageDefinition}
Expand Down Expand Up @@ -135,6 +141,14 @@ export const ZosFilesMessages: { [key: string]: IMessageDefinition } = {
message: "Specify the input directory path."
},

/**
* Message indicating that a new target data set was created and copied into
* @type {IMessageDefinition}
*/
dataSetCopiedIntoNew: {
message: `Source contents were successfully copied into a new data set - %s`
},

/**
* Message indicating that the data set was created successfully
* @type {IMessageDefinition}
Expand Down
Loading

0 comments on commit 97a3c68

Please sign in to comment.