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

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 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
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` method now creates a new data set if the inputted target data set does not exist. [#2386](https://github.com/zowe/zowe-cli/pull/2386)
traeok marked this conversation as resolved.
Show resolved Hide resolved

## `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 @@ -61,6 +61,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)}`);
}
});
pujal0909 marked this conversation as resolved.
Show resolved Hide resolved
describe("Sequential > Sequential", () => {
beforeEach(async () => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ 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, "dataSetExists").mockResolvedValue(true);
});
afterAll(() => {
isPDSSpy.mockRestore();
dataSetExistsSpy.mockRestore();
});
describe("Success Scenarios", () => {
describe("Sequential > Sequential", () => {
Expand Down Expand Up @@ -456,13 +459,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, "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 All @@ -471,11 +485,12 @@ describe("Copy", () => {
dsn:fromDataSetName
}}
);
const newDataSet = false;
expect(isPDSSpy).toHaveBeenNthCalledWith(1, dummySession, fromDataSetName);
expect(isPDSSpy).toHaveBeenNthCalledWith(2, dummySession, toDataSetName);

expect(copyPDSSpy).toHaveBeenCalledTimes(1);
expect(copyPDSSpy).toHaveBeenCalledWith(dummySession, fromDataSetName, toDataSetName);
expect(copyPDSSpy).toHaveBeenCalledWith(dummySession, fromDataSetName, toDataSetName, newDataSet);

expect(response).toEqual({
success: true,
Expand Down Expand Up @@ -524,6 +539,36 @@ 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: ZosFilesMessages.datasetCopiedSuccessfully.message
});
});
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
});
});
});
});
describe("Failure Scenarios", () => {
Expand Down Expand Up @@ -662,7 +707,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.dataSetExists(dummySession, dsPO.dsname);
}
catch(e) {
caughtError = e;
Dismissed Show dismissed Hide dismissed
}
expect(response).toEqual(true);
expect(listDatasetSpy).toHaveBeenCalledWith(dummySession, dsPO.dsname, { attributes: true , 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.dataSetExists(dummySession, dsPO.dsname);
}
catch(e) {
caughtError = e;
Dismissed Show dismissed Hide dismissed
}
expect(response).toEqual(false);
expect(listDatasetSpy).toHaveBeenCalledWith(dummySession, dsPO.dsname, { attributes: true , start: dsPO.dsname});
});
it("should successfully copy members from source to target PDS", async () => {
let caughtError;
let response;
Expand All @@ -675,7 +760,7 @@ describe("Copy", () => {
}
};
const fileList = ["mem1", "mem2"];

const newDataSet = false;
listAllMembersSpy.mockImplementation(async (): Promise<any> => sourceResponse);
downloadAllMembersSpy.mockImplementation(async (): Promise<any> => undefined);
fileListPathSpy.mockReturnValue(fileList);
Expand All @@ -686,7 +771,7 @@ describe("Copy", () => {


try{
response = await Copy.copyPDS(dummySession, fromDataSetName, toDataSetName);
response = await Copy.copyPDS(dummySession, fromDataSetName, toDataSetName, newDataSet);
}
catch(e) {
caughtError = e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ Destination: %s",
"missingZfsOption": Object {
"message": "To create a z/OS file system, the following option must be supplied: ",
},
"newDataSetCreated": Object {
"message": "New data set created",
},
"noDataSetsInList": Object {
"message": "No data sets left after excluded pattern(s) were filtered out.",
},
Expand Down
8 changes: 8 additions & 0 deletions packages/zosfiles/src/constants/ZosFiles.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,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}
*/
newDataSetCreated: {
message: "New data set created"
},

/**
* Message indicating that the data set was created successfully
* @type {IMessageDefinition}
Expand Down
37 changes: 29 additions & 8 deletions packages/zosfiles/src/methods/copy/Copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,20 @@
ImperativeExpect.toBeDefinedAndNonBlank(options["from-dataset"].dsn, "fromDataSetName");
ImperativeExpect.toBeDefinedAndNonBlank(toDataSetName, "toDataSetName");

const targetDataSetExists = await this.dataSetExists(session, toDataSetName);

let newDataSet = false;
if(!targetDataSetExists) {
newDataSet = true;
pujal0909 marked this conversation as resolved.
Show resolved Hide resolved
await Create.dataSetLike(session, toDataSetName, options["from-dataset"].dsn);
}
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) {
return await this.copyPDS(session, options["from-dataset"].dsn, toDataSetName);
return await this.copyPDS(session, options["from-dataset"].dsn, toDataSetName, newDataSet);
}
}

const endpoint: string = posix.join(
ZosFilesConstants.RESOURCE,
ZosFilesConstants.RES_DS_FILES,
Expand Down Expand Up @@ -106,7 +112,7 @@
}

/**
* Private function that checks if a dataset is type PDS
* Function that checks if a dataset is type PDS
**/
public static async isPDS(
session: AbstractSession,
Expand All @@ -123,6 +129,22 @@
}
}

/**
* Function that checks if the data set exists
**/
public static async dataSetExists(
pujal0909 marked this conversation as resolved.
Show resolved Hide resolved
session: AbstractSession,
dataSetName: string
): Promise<boolean> {
let dsnameIndex;
const dataSetList = await List.dataSet(session, dataSetName, {attributes: true, start: dataSetName});
pujal0909 marked this conversation as resolved.
Show resolved Hide resolved
if(dataSetList.apiResponse != null && dataSetList.apiResponse.returnedRows != null && dataSetList.apiResponse.items != null) {
dsnameIndex = dataSetList.apiResponse.returnedRows === 0 ? -1 :
dataSetList.apiResponse.items.findIndex((ds: any) => ds.dsname.toUpperCase() === dataSetName.toUpperCase());
}
return dsnameIndex !== -1;
}

/**
* Copy the members of a Partitioned dataset into another Partitioned dataset
*
Expand All @@ -139,10 +161,7 @@
*/

public static async copyPDS (
session: AbstractSession,
fromPds: string,
toPds: string
): Promise<IZosFilesResponse> {
session: AbstractSession, fromPds: string, toPds: string, newDataSet: boolean): Promise<IZosFilesResponse> {
pujal0909 marked this conversation as resolved.
Show resolved Hide resolved
try {
const sourceResponse = await List.allMembers(session, fromPds);
const sourceMemberList: Array<{ member: string }> = sourceResponse.apiResponse.items;
Expand All @@ -166,7 +185,9 @@
fs.rmSync(downloadDir, {recursive: true});
return {
success:true,
commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message
commandResponse: newDataSet
? ZosFilesMessages.newDataSetCreated.message + ` - "${toPds}"`

Check warning on line 189 in packages/zosfiles/src/methods/copy/Copy.ts

View check run for this annotation

Codecov / codecov/patch

packages/zosfiles/src/methods/copy/Copy.ts#L189

Added line #L189 was not covered by tests
pujal0909 marked this conversation as resolved.
Show resolved Hide resolved
: ZosFilesMessages.datasetCopiedSuccessfully.message
};
}
catch (error) {
Expand Down
Loading