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

Port etag to master #646

Merged
merged 13 commits into from
Feb 10, 2020
23 changes: 23 additions & 0 deletions packages/rest/src/ZosmfHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,27 @@ export class ZosmfHeaders {
public static readonly X_IBM_MIGRATED_RECALL_NO_WAIT: IHeaderContent = {"X-IBM-Migrated-Recall": "nowait"};
public static readonly X_IBM_MIGRATED_RECALL_ERROR: IHeaderContent = {"X-IBM-Migrated-Recall": "error"};

/**
* Header to check ETag on read
* Request returns HTTP 304 if not modified
* @static
* @memberof ZosmfHeaders
*/
public static readonly IF_NONE_MATCH = "If-None-Match";

/**
* Header to check ETag on write
* Request returns HTTP 412 if not matched
* @static
* @memberof ZosmfHeaders
*/
public static readonly IF_MATCH = "If-Match";

/**
* Header to force return of ETag in response regardless of file size
* By default Etag is returned only for files smaller than a system determined value (which is at least 8mb)
* @static
* @memberof ZosmfHeaders
*/
public static readonly X_IBM_RETURN_ETAG: IHeaderContent = {"X-IBM-Return-Etag": "true"};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
CreateDataSetTypeEnum,
Delete,
Download,
Upload,
IDownloadOptions,
IZosFilesResponse,
ZosFilesConstants,
Expand Down Expand Up @@ -148,6 +149,41 @@ describe("Download Data Set", () => {
file = dsname.replace(regex, "/") + ".txt";
});

it("should download a data set and return Etag", async () => {
let error;
let response: IZosFilesResponse;

const data: string = "abcdefghijklmnopqrstuvwxyz";
const endpoint: string = ZosFilesConstants.RESOURCE + ZosFilesConstants.RES_DS_FILES + "/" + dsname;
await ZosmfRestClient.putExpectString(REAL_SESSION, endpoint, [], data);

const options: IDownloadOptions = {
returnEtag: true
};

try {
response = await Download.dataSet(REAL_SESSION, dsname, options);
Imperative.console.info("Response: " + inspect(response));
} catch (err) {
error = err;
Imperative.console.info("Error: " + inspect(error));
}

expect(error).toBeFalsy();
expect(response).toBeTruthy();
expect(response.success).toBeTruthy();
expect(response.commandResponse).toContain(
ZosFilesMessages.datasetDownloadedSuccessfully.message.substring(0, "Data set downloaded successfully".length + 1));
expect(response.apiResponse.etag).toBeDefined();
// convert the data set name to use as a path/file for clean up in AfterEach
const regex = /\./gi;
file = dsname.replace(regex, "/") + ".txt";

// Compare the downloaded contents to those uploaded
const fileContents = stripNewLines(readFileSync(`${file}`).toString());
expect(fileContents).toEqual(data);
});

it("should download a data set that has been populated by upload and use file extension specified", async () => {
let error;
let response: IZosFilesResponse;
Expand Down Expand Up @@ -448,6 +484,61 @@ describe("Download Data Set", () => {

});

it("should download uss file and return Etag", async () => {
let error;
let response: IZosFilesResponse;

const data: string = "abcdefghijklmnopqrstuvwxyz";
const endpoint: string = ZosFilesConstants.RESOURCE + ZosFilesConstants.RES_USS_FILES + ussname;

(await ZosmfRestClient.putExpectString(REAL_SESSION, endpoint, [], data));

const options: IDownloadOptions = {
returnEtag: true
};

try {
response = await Download.ussFile(REAL_SESSION, ussname, options);
} catch (err) {
error = err;
}
expect(error).toBeFalsy();
expect(response).toBeTruthy();
expect(response.apiResponse.etag).toBeDefined();
// Compare the downloaded contents to those uploaded
const fileContents = stripNewLines(readFileSync(`./${posix.basename(ussname)}`).toString());
expect(fileContents).toEqual(data);

});

// When requesting etag, z/OSMF has a limit on file size when it stops to return etag by default (>8mb)
// We are passing X-IBM-Return-Etag to force z/OSMF to always return etag, but testing here for case where it would be optional
it("should download a 10mb uss file and return Etag", async () => {
let error;
let response: IZosFilesResponse;

// Create a 10 mb buffer
const bufferSize = 10000000;
const buffer = new ArrayBuffer(bufferSize);
const data = Buffer.from(buffer);

(await Upload.bufferToUSSFile(REAL_SESSION, ussname, data));

const options: IDownloadOptions = {
returnEtag: true
};

try {
response = await Download.ussFile(REAL_SESSION, ussname, options);
} catch (err) {
error = err;
}
expect(error).toBeFalsy();
expect(response).toBeTruthy();
expect(response.apiResponse.etag).toBeDefined();
Imperative.console.info(response.apiResponse.etag);
});

it("should download uss file content in binary", async () => {
let error;
let response: IZosFilesResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
*/

import { Create, CreateDataSetTypeEnum, Delete, IUploadOptions, IZosFilesResponse, Upload, ZosFilesMessages } from "../../../../../";
import { Create, CreateDataSetTypeEnum, Delete, IUploadOptions, IZosFilesResponse, Upload, ZosFilesMessages, Download } from "../../../../../";
import { Imperative, Session } from "@zowe/imperative";
import { inspect } from "util";
import { ITestEnvironment } from "../../../../../../../__tests__/__src__/environment/doc/response/ITestEnvironment";
Expand All @@ -19,6 +19,7 @@ import { getUniqueDatasetName, stripNewLines } from "../../../../../../../__test
import { Get, ZosFilesConstants } from "../../../../../index";
import { ZosmfRestClient } from "../../../../../../rest";
import { IUploadMap } from "../../../../../src/api/methods/upload/doc/IUploadMap";
import * as fs from "fs";

let REAL_SESSION: Session;
let testEnvironment: ITestEnvironment;
Expand Down Expand Up @@ -55,6 +56,8 @@ describe("Upload Data Set", () => {
beforeEach(async () => {
let error;
let response;
uploadOptions.etag = undefined;
uploadOptions.returnEtag = undefined;

try {
response = await Create.dataSet(REAL_SESSION,
Expand Down Expand Up @@ -94,6 +97,54 @@ describe("Upload Data Set", () => {
expect(response.commandResponse).toContain(ZosFilesMessages.dataSetUploadedSuccessfully.message);
});

it("should upload a file to a physical sequential data set while passing correct Etag", async () => {
let error;
let response: IZosFilesResponse;

// first we have to get the Etag, so we can compare it. We do it by preemtively downloading the file and requesting Etag
await Upload.fileToDataset(REAL_SESSION, __dirname + "/testfiles/upload.txt", dsname);
const downloadOptions = {file: __dirname + "/testfiles/upload.txt", returnEtag: true};
const downloadResponse = await Download.dataSet(REAL_SESSION, dsname, downloadOptions);
expect(downloadResponse.success).toBeTruthy();
expect(downloadResponse.apiResponse.etag).toBeDefined();

try {
uploadOptions.etag = downloadResponse.apiResponse.etag;
// packages/zosfiles/__tests__/__system__/api/methods/upload/
response = await Upload.fileToDataset(REAL_SESSION, __dirname + "/testfiles/upload.txt", dsname, uploadOptions);
Imperative.console.info("Response: " + inspect(response));
} catch (err) {
error = err;
Imperative.console.info("Error: " + inspect(error));
}

expect(error).toBeFalsy();
expect(response).toBeTruthy();
expect(response.success).toBeTruthy();
expect(response.commandResponse).toContain(ZosFilesMessages.dataSetUploadedSuccessfully.message);
});

it("should upload a file to a physical sequential data set and return the Etag", async () => {
let error;
let response: IZosFilesResponse;
uploadOptions.returnEtag = true;

try {
// packages/zosfiles/__tests__/__system__/api/methods/upload/
response = await Upload.fileToDataset(REAL_SESSION,
__dirname + "/testfiles/upload.txt", dsname, uploadOptions);
Imperative.console.info("Response: " + inspect(response));
} catch (err) {
error = err;
Imperative.console.info("Error: " + inspect(error));
}
expect(error).toBeFalsy();
expect(response).toBeTruthy();
expect(response.success).toBeTruthy();
expect(response.commandResponse).toContain(ZosFilesMessages.dataSetUploadedSuccessfully.message);
expect(response.apiResponse[0].etag).toBeDefined();
});

it("should upload a file to a physical sequential data set using relative path", async () => {
let error;
let response: IZosFilesResponse;
Expand Down Expand Up @@ -448,6 +499,11 @@ describe("Upload USS file", () => {

describe("Success Scenarios", () => {

beforeEach(async () => {
uploadOptions.etag = undefined;
fs.writeFileSync(inputfile, testdata);
});

afterEach(async () => {
let error;
let response;
Expand Down Expand Up @@ -502,7 +558,6 @@ describe("Upload USS file", () => {
let uploadResponse;
let getResponse;


try {
uploadResponse = await Upload.fileToUSSFile(REAL_SESSION, inputfile, ussname);
getResponse = await Get.USSFile(REAL_SESSION, ussname);
Expand Down Expand Up @@ -533,6 +588,47 @@ describe("Upload USS file", () => {
expect(getResponse).toEqual(Buffer.from(testdata));

});
it("should upload a USS file while passing correct Etag", async () => {
let error;
let uploadResponse;

// first we have to get the Etag, so we can compare it. We do it by preemtively downloading the file and requesting Etag
await Upload.fileToUssFile(REAL_SESSION, inputfile, ussname, {returnEtag: false});
const downloadResponse = await Download.ussFile(REAL_SESSION, ussname, {file: inputfile, returnEtag: true});
expect(downloadResponse.success).toBeTruthy();
expect(downloadResponse.apiResponse.etag).toBeDefined();

try {
uploadResponse = await Upload.fileToUssFile(REAL_SESSION, inputfile, ussname, {etag: downloadResponse.apiResponse.etag});
Imperative.console.info("Response: " + inspect(uploadResponse));
} catch (err) {
error = err;
Imperative.console.info("Error: " + inspect(error));
}

expect(error).toBeFalsy();
expect(uploadResponse).toBeTruthy();
expect(uploadResponse.success).toBeTruthy();
expect(uploadResponse.commandResponse).toContain(ZosFilesMessages.ussFileUploadedSuccessfully.message);
});
it("should upload a USS file and return Etag", async () => {
let error;
let uploadResponse;
let getResponse;

try {
uploadResponse = await Upload.fileToUssFile(REAL_SESSION, inputfile, ussname, {returnEtag: true});
getResponse = await Get.USSFile(REAL_SESSION, ussname);
} catch (err) {
error = err;
Imperative.console.info("Error: " + inspect(error));
}

expect(error).toBeFalsy();
expect(uploadResponse).toBeTruthy();
expect(uploadResponse.success).toBeTruthy();
expect(uploadResponse.apiResponse.etag).toBeDefined();
});
});
});

Expand Down
Loading