Skip to content

Commit

Permalink
Merge pull request #646 from zowe/port_etag_to_master
Browse files Browse the repository at this point in the history
Port etag to master
  • Loading branch information
zFernand0 authored Feb 10, 2020
2 parents 3b44bb7 + 4be6a96 commit debd676
Show file tree
Hide file tree
Showing 15 changed files with 1,713 additions and 187 deletions.
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

0 comments on commit debd676

Please sign in to comment.