Skip to content

Enhancement/#311 page iterator request options #318

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

Merged
merged 12 commits into from
Sep 25, 2020
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
123 changes: 123 additions & 0 deletions spec/development/workload/PageIterator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

import { assert } from "chai";
import { Event } from "microsoft-graph";

import { PageIterator, PageIteratorCallback, GraphRequestOptions, PageCollection } from "../../../src/tasks/PageIterator";
import { getClient } from "../test-helper";
import { ChaosHandler } from "../../../src/middleware/ChaosHandler";
import { ChaosHandlerOptions } from "../../../src/middleware/options/ChaosHandlerOptions";
import { ChaosStrategy } from "../../../src/middleware/options/ChaosStrategy";
import { Client, ClientOptions } from "../../../src";

const client = getClient();
describe("PageIterator", function() {
const pstHeader = { Prefer: 'outlook.timezone= "pacific standard time"' };
const utc = "UTC";
const pst = "Pacific Standard Time";
const testURL = "/me/events";

before(async function() {
this.timeout(20000);

const response = await client.api(testURL).get();
const numberOfEvents = 4;
const existingEventsCount = response.value.length;

if (existingEventsCount >= numberOfEvents) {
return;
}
const eventSubject = '"subject": "Test event ';
const eventTimeZone = '"timeZone": "UTC"';
const eventStartDateTime = '"start": { "dateTime":"' + new Date().toISOString() + '",' + eventTimeZone + "}";
const eventEndDateTime = '"end": { "dateTime":"' + new Date().toISOString() + '",' + eventTimeZone + "}";

for (let i = 1; i <= numberOfEvents - existingEventsCount; i++) {
const eventBody = "{" + eventSubject + "" + 1 + '",' + eventStartDateTime + "," + eventEndDateTime + "}";
const response = await client.api(testURL).post(eventBody);
if (response.error) {
throw response.error;
}
}
});

it("same headers passed with pageIterator", async () => {
const response = await client
.api(`${testURL}?$top=2`)
.headers(pstHeader)
.select("id,start,end")
.get();

const callback: PageIteratorCallback = (eventResponse) => {
const event = eventResponse as Event;
assert.equal(event.start.timeZone, pst);
return true;
};
var requestOptions: GraphRequestOptions = { options: { headers: pstHeader } };
if (response["@odata.nextLink"]) {
const pageIterator = new PageIterator(client, response, callback, requestOptions);
await pageIterator.iterate();
assert.isTrue(pageIterator.isComplete());
}
}).timeout(30 * 1000);

it("different headers passed with pageIterator", async () => {
const response = await client
.api(`${testURL}?$top=2`)
.headers({ Prefer: `outlook.timezone= "${utc}"` })
.select("id,start,end")
.get();

let counter = 0;
const callback: PageIteratorCallback = (eventResponse) => {
const event = eventResponse as Event;
if (counter < 2) {
assert.equal(event.start.timeZone, utc);
counter++;
} else {
assert.equal(event.start.timeZone, pst);
}
return true;
};

var requestOptions = { headers: pstHeader };
if (response["@odata.nextLink"]) {
const pageIterator = new PageIterator(client, response, callback, requestOptions);
await pageIterator.iterate();
assert.isTrue(pageIterator.isComplete());
}
}).timeout(30 * 1000);

it("setting middleware with pageIterator", async () => {
const middleware = new ChaosHandler();
const getPageCollection = () => {
return {
value: [],
"@odata.nextLink": "nextURL",
additionalContent: "additional content",
};
};
const clientOptions: ClientOptions = {
middleware,
};
const responseBody = { value: [{ event1: "value1" }, { event2: "value2" }] };
let counter = 1;
const callback: PageIteratorCallback = (data) => {
assert.equal(data["event" + counter], "value" + counter);
counter++;
return true;
};

const middlewareOptions = [new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200, "middleware options for pageIterator", 0, responseBody)];
const requestOptions = { middlewareOptions };

const client = Client.initWithMiddleware(clientOptions);
const pageIterator = new PageIterator(client, getPageCollection(), callback, requestOptions);
await pageIterator.iterate();
});
});
8 changes: 3 additions & 5 deletions src/GraphRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ export class GraphRequest {
* @private
* A member to hold custom header options for a request
*/
private _headers: {
[key: string]: string;
};
private _headers: HeadersInit;

/**
* @private
Expand Down Expand Up @@ -428,10 +426,10 @@ export class GraphRequest {
/**
* @public
* Sets the custom headers for a request
* @param {KeyValuePairObjectStringNumber} headers - The headers key value pair object
* @param {KeyValuePairObjectStringNumber | HeadersInit} headers - The request headers
* @returns The same GraphRequest instance that is being called with
*/
public headers(headers: KeyValuePairObjectStringNumber): GraphRequest {
public headers(headers: KeyValuePairObjectStringNumber | HeadersInit): GraphRequest {
for (const key in headers) {
if (headers.hasOwnProperty(key)) {
this._headers[key] = headers[key] as string;
Expand Down
16 changes: 10 additions & 6 deletions src/middleware/ChaosHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,19 @@ export class ChaosHandler implements Middleware {
* @param {string} statusMessage - the status message to be returned for the request
* @param {string} requestID - request id
* @param {string} requestDate - date of the request
* @param {any?} requestBody - the request body to be returned for the request
* @returns response body
*/
private createResponseBody(statusCode: number, statusMessage: string, requestID: string, requestDate: string) {
let responseBody: any;
private createResponseBody(statusCode: number, statusMessage: string, requestID: string, requestDate: string, responseBody?: any) {
if (responseBody) {
return responseBody;
}
let body: any;
if (statusCode >= 400) {
const codeMessage: string = httpStatusCode[statusCode];
const errMessage: string = statusMessage;

responseBody = {
body = {
error: {
code: codeMessage,
message: errMessage,
Expand All @@ -110,9 +114,9 @@ export class ChaosHandler implements Middleware {
},
};
} else {
responseBody = {};
body = {};
}
return responseBody;
return body;
}

/**
Expand All @@ -132,7 +136,7 @@ export class ChaosHandler implements Middleware {
requestID = generateUUID();
requestDate = new Date();
responseHeader = this.createResponseHeaders(chaosHandlerOptions.statusCode, requestID, requestDate.toString());
responseBody = this.createResponseBody(chaosHandlerOptions.statusCode, chaosHandlerOptions.statusMessage, requestID, requestDate.toString());
responseBody = this.createResponseBody(chaosHandlerOptions.statusCode, chaosHandlerOptions.statusMessage, requestID, requestDate.toString(), chaosHandlerOptions.responseBody);
const init: any = { url: requestURL, status: chaosHandlerOptions.statusCode, statusText: chaosHandlerOptions.statusMessage, headers: responseHeader };
context.response = new Response(responseBody, init);
} catch (error) {
Expand Down
18 changes: 14 additions & 4 deletions src/middleware/options/ChaosHandlerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { MiddlewareOptions } from "./IMiddlewareOptions";
*/
export class ChaosHandlerOptions implements MiddlewareOptions {
/**
* Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL
* Specifies the startegy used for the Testing Handler -> RANDOM/MANUAL
*
* @public
*/
Expand Down Expand Up @@ -48,20 +48,30 @@ export class ChaosHandlerOptions implements MiddlewareOptions {
*/
public chaosPercentage: number;

/**
* The response body to be returned in the response
*
* @public
*/
public responseBody: any;

/**
* @public
* @constructor
* To create an instance of Testing Handler Options
* @param {ChaosStrategy} ChaosStrategy - Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL
* @param {number?} statusCode - The Message to be returned in the response
* @param {string} - The Message to be returned in the response
* @param {number?} statusCode - The statusCode to be returned in the response
* @param {string} statusMessage - The Message to be returned in the response
* @param {number?} chaosPercentage - The percentage of randomness/chaos in the handler
* @param {any?} responseBody - The response body to be returned in the response
* @returns An instance of ChaosHandlerOptions
*/
public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusCode?: number, statusMessage: string = "Some error Happened", chaosPercentage?: number) {
public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusCode?: number, statusMessage: string = "Some error Happened", chaosPercentage?: number, responseBody?: any) {
this.chaosStrategy = chaosStrategy;
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.chaosPercentage = chaosPercentage !== undefined ? chaosPercentage : 10;
this.responseBody = responseBody;
if (this.chaosPercentage > 100) {
throw new Error("Error Pecentage can not be more than 100");
}
Expand Down
40 changes: 38 additions & 2 deletions src/tasks/PageIterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
* @module PageIterator
*/

import { FetchOptions } from "../IFetchOptions";
import { Client } from "../index";
import { MiddlewareOptions } from "../middleware/options/IMiddlewareOptions";
import { ResponseType } from "../ResponseType";

/**
* Signature representing PageCollection
Expand All @@ -25,6 +28,19 @@ export interface PageCollection {
[Key: string]: any;
}

/**
* Signature to define the request options to be sent during request.
* The values of the GraphRequestOptions properties are passed to the Graph Request object.
* @property {HeadersInit} headers - the header options for the request
* @property {MiddlewareOptions[]} middlewareoptions - The middleware options for the request
* @property {FetchOptions} options - The fetch options for the request
*/
export interface GraphRequestOptions {
headers?: HeadersInit;
middlewareOptions?: MiddlewareOptions[];
options?: FetchOptions;
}

/**
* Signature representing callback for page iterator
* @property {Function} callback - The callback function which should return boolean to continue the continue/stop the iteration.
Expand Down Expand Up @@ -73,22 +89,29 @@ export class PageIterator {
*/
private complete: boolean;

/**
* Information to be added to the request
*/
private requestOptions: GraphRequestOptions;

/**
* @public
* @constructor
* Creates new instance for PageIterator
* @param {Client} client - The graph client instance
* @param {PageCollection} pageCollection - The page collection object
* @param {PageIteratorCallback} callBack - The callback function
* @param {GraphRequestOptions} requestOptions - The request options
* @returns An instance of a PageIterator
*/
public constructor(client: Client, pageCollection: PageCollection, callback: PageIteratorCallback) {
public constructor(client: Client, pageCollection: PageCollection, callback: PageIteratorCallback, requestOptions?: GraphRequestOptions) {
this.client = client;
this.collection = pageCollection.value;
this.nextLink = pageCollection["@odata.nextLink"];
this.deltaLink = pageCollection["@odata.deltaLink"];
this.callback = callback;
this.complete = false;
this.requestOptions = requestOptions;
}

/**
Expand Down Expand Up @@ -116,7 +139,20 @@ export class PageIterator {
*/
private async fetchAndUpdateNextPageData(): Promise<any> {
try {
const response: PageCollection = await this.client.api(this.nextLink).get();
let graphRequest = this.client.api(this.nextLink);
if (this.requestOptions) {
if (this.requestOptions.headers) {
graphRequest = graphRequest.headers(this.requestOptions.headers);
}
if (this.requestOptions.middlewareOptions) {
graphRequest = graphRequest.middlewareOptions(this.requestOptions.middlewareOptions);
}
if (this.requestOptions.options) {
graphRequest = graphRequest.options(this.requestOptions.options);
}
}

const response: PageCollection = await graphRequest.get();
this.collection = response.value;
this.nextLink = response["@odata.nextLink"];
this.deltaLink = response["@odata.deltaLink"];
Expand Down