Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

copy pds into new target data set enhancement #2393

Merged
merged 25 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'"
t1m0thyj marked this conversation as resolved.
Show resolved Hide resolved
}
},
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;
Dismissed Show dismissed Hide dismissed
}
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;
Dismissed Show dismissed Hide dismissed
}
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
Loading