Skip to content
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
40 changes: 40 additions & 0 deletions spec/core/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
import { assert } from "chai";
import "isomorphic-fetch";

import { CustomAuthenticationProvider, TelemetryHandler } from "../../src";
import { Client } from "../../src/Client";
import { AuthProvider } from "../../src/IAuthProvider";
import { ClientOptions } from "../../src/IClientOptions";
import { Options } from "../../src/IOptions";
import { AuthenticationHandler } from "../../src/middleware/AuthenticationHandler";
import { ChaosHandler } from "../../src/middleware/ChaosHandler";
import { ChaosHandlerOptions } from "../../src/middleware/options/ChaosHandlerOptions";
import { ChaosStrategy } from "../../src/middleware/options/ChaosStrategy";
import { DummyAuthenticationProvider } from "../DummyAuthenticationProvider";
import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler";

Expand Down Expand Up @@ -61,6 +66,41 @@ describe("Client.ts", () => {
assert.equal(error.name, "InvalidMiddlewareChain");
}
});

it("Init middleware using a middleware array", async () => {
const provider: AuthProvider = (done) => {
done(null, "dummy_token");
};
const authHandler = new AuthenticationHandler(new CustomAuthenticationProvider(provider));
const responseBody = "Test response body";
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Testing middleware array", 200, 0, responseBody);
const middlewareArray = [authHandler, new ChaosHandler(options)];
const client = Client.initWithMiddleware({ middleware: middlewareArray });

const response = await client.api("me").get();
assert.equal(response, responseBody);
});

it("Init middleware using a chained middleware array", async () => {
const provider: AuthProvider = (done) => {
done(null, "dummy_token");
};
const authHandler = new AuthenticationHandler(new CustomAuthenticationProvider(provider));

const responseBody = "Test response body";
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Testing chained middleware array", 200, 0, responseBody);
const chaosHandler = new ChaosHandler(options);
const telemetryHandler = new TelemetryHandler();

authHandler.setNext(telemetryHandler);
telemetryHandler.setNext(chaosHandler);

const middlewareArray = [authHandler];
const client = Client.initWithMiddleware({ middleware: middlewareArray });

const response = await client.api("me").get();
assert.equal(response, responseBody);
});
});

describe("init", () => {
Expand Down
31 changes: 31 additions & 0 deletions spec/core/HTTPClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,38 @@ describe("HTTPClient.ts", () => {
assert.isDefined(httpClient["middleware"]);
assert.equal(httpClient["middleware"], httpMessageHandler);
});

it("Should create an instance and populate middleware member when passing a middleware array", () => {
const client = new HTTPClient(...[httpMessageHandler]);
assert.isDefined(client["middleware"]);
assert.equal(client["middleware"], httpMessageHandler);
});

it("Should throw an error if middleware is undefined", () => {
try {
const client = new HTTPClient();
} catch (error) {
assert.equal(error.name, "InvalidMiddlewareChain");
}
});

it("Should throw an error if middleware is passed as null", () => {
try {
const client = new HTTPClient(null);
} catch (error) {
assert.equal(error.name, "InvalidMiddlewareChain");
}
});

it("Should throw an error if middleware is passed as an empty array", () => {
try {
const client = new HTTPClient(...[]);
} catch (error) {
assert.equal(error.name, "InvalidMiddlewareChain");
}
});
});

/* tslint:enable: no-string-literal */

describe("sendRequest", async () => {
Expand Down
27 changes: 27 additions & 0 deletions spec/middleware/MiddlewareFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* -------------------------------------------------------------------------------------------
* 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 { AuthenticationHandler, CustomAuthenticationProvider, HTTPMessageHandler, RedirectHandler, RetryHandler, TelemetryHandler } from "../../src";
import { AuthProvider } from "../../src/IAuthProvider";
import { MiddlewareFactory } from "../../src/middleware/MiddlewareFactory";

describe("MiddlewareFactory", () => {
it("Should return the default pipeline", () => {
const provider: AuthProvider = (done) => {
done(null, "dummy_token");
};
const defaultMiddleWareArray = MiddlewareFactory.getDefaultMiddlewareChain(new CustomAuthenticationProvider(provider));

assert.isTrue(defaultMiddleWareArray[0] instanceof AuthenticationHandler);
assert.isTrue(defaultMiddleWareArray[1] instanceof RetryHandler);
assert.isTrue(defaultMiddleWareArray[2] instanceof RedirectHandler);
assert.isTrue(defaultMiddleWareArray[3] instanceof TelemetryHandler);
assert.isTrue(defaultMiddleWareArray[4] instanceof HTTPMessageHandler);
});
});
2 changes: 1 addition & 1 deletion src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class Client {
} else if (clientOptions.authProvider !== undefined) {
httpClient = HTTPClientFactory.createWithAuthenticationProvider(clientOptions.authProvider);
} else if (clientOptions.middleware !== undefined) {
httpClient = new HTTPClient(clientOptions.middleware);
httpClient = new HTTPClient(...[].concat(clientOptions.middleware));
} else {
const error = new Error();
error.name = "InvalidMiddlewareChain";
Expand Down
42 changes: 39 additions & 3 deletions src/HTTPClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,46 @@ export class HTTPClient {
* @public
* @constructor
* Creates an instance of a HTTPClient
* @param {Middleware} middleware - The first middleware of the middleware chain
* @param {...Middleware} middleware - The first middleware of the middleware chain or a sequence of all the Middleware handlers
*/
public constructor(middleware: Middleware) {
this.middleware = middleware;
public constructor(...middleware: Middleware[]) {
if (!middleware || !middleware.length) {
const error = new Error();
error.name = "InvalidMiddlewareChain";
error.message = "Please provide a default middleware chain or custom middleware chain";
throw error;
}
this.setMiddleware(...middleware);
}

/**
* @private
* Processes the middleware parameter passed to set this.middleware property
* @param {...Middleware} middleware - The middleware passed
* @returns Nothing
*/
private setMiddleware(...middleware: Middleware[]): void {
if (middleware.length > 1) {
this.parseMiddleWareArray(middleware);
} else {
this.middleware = middleware[0];
}
}

/**
* @private
* Processes the middleware array to construct the chain
* and sets this.middleware property to the first middlware handler of the array
* @param {Middleware[]} middlewareArray - The array of middleware handlers
* @returns Nothing
*/
private parseMiddleWareArray(middlewareArray: Middleware[]) {
middlewareArray.forEach((element, index) => {
if (index < middlewareArray.length - 1) {
element.setNext(middlewareArray[index + 1]);
}
});
this.middleware = middlewareArray[0];
}

/**
Expand Down
9 changes: 5 additions & 4 deletions src/HTTPClientFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { TelemetryHandler } from "./middleware/TelemetryHandler";
* @returns A boolean representing the environment is node or not
*/
const isNodeEnvironment = (): boolean => {
return new Function("try {return this === global;}catch(e){return false;}")(); // tslint:disable-line: function-constructor
return typeof process === "object" && typeof require === "function";
};

/**
Expand Down Expand Up @@ -69,10 +69,11 @@ export class HTTPClientFactory {
* @public
* @static
* Creates a middleware chain with the given one
* @param {Middleware} middleware - The first middleware of the middleware chain
* @property {...Middleware} middleware - The first middleware of the middleware chain or a sequence of all the Middleware handlers
* @returns A HTTPClient instance
*/
public static createWithMiddleware(middleware: Middleware): HTTPClient {
return new HTTPClient(middleware);
public static createWithMiddleware(...middleware: Middleware[]): HTTPClient {
// Middleware should not empty or undefined. This is check is present in the HTTPClient constructor.
return new HTTPClient(...middleware);
}
}
4 changes: 2 additions & 2 deletions src/IClientOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import { Middleware } from "./middleware/IMiddleware";
* @property {boolean} [debugLogging] - The boolean to enable/disable debug logging
* @property {string} [defaultVersion] - The default version that needs to be used while making graph api request
* @property {FetchOptions} [fetchOptions] - The options for fetch request
* @property {Middleware} [middleware] - The first middleware of the middleware chain
* @property {Middleware| Middleware[]} [middleware] - The first middleware of the middleware chain or an array of the Middleware handlers
*/
export interface ClientOptions {
authProvider?: AuthenticationProvider;
baseUrl?: string;
debugLogging?: boolean;
defaultVersion?: string;
fetchOptions?: FetchOptions;
middleware?: Middleware;
middleware?: Middleware | Middleware[];
}
6 changes: 5 additions & 1 deletion src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,21 @@ export * from "../middleware/HTTPMessageHandler";
export * from "../middleware/IMiddleware";
export * from "../middleware/RetryHandler";
export * from "../middleware/TelemetryHandler";

export * from "../middleware/MiddlewareFactory";
export * from "../middleware/options/AuthenticationHandlerOptions";
export * from "../middleware/options/IMiddlewareOptions";
export * from "../middleware/options/RetryHandlerOptions";
export * from "../middleware/options/TelemetryHandlerOptions";
export * from "../middleware/options/ChaosHandlerOptions";
export * from "../middleware/options/ChaosStrategy";
export * from "../middleware/ChaosHandler";

export * from "../tasks/LargeFileUploadTask";
export * from "../tasks/OneDriveLargeFileUploadTask";
export * from "../tasks/PageIterator";

export * from "../Client";
export * from "../CustomAuthenticationProvider";
export * from "../GraphError";
export * from "../GraphRequest";
export * from "../IAuthProvider";
Expand Down
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,22 @@ export * from "./middleware/IMiddleware";
export * from "./middleware/RetryHandler";
export * from "./middleware/RedirectHandler";
export * from "./middleware/TelemetryHandler";

export * from "./middleware/MiddlewareFactory";
export * from "./middleware/options/AuthenticationHandlerOptions";
export * from "./middleware/options/IMiddlewareOptions";
export * from "./middleware/options/RetryHandlerOptions";
export * from "./middleware/options/RedirectHandlerOptions";
export * from "./middleware/options/TelemetryHandlerOptions";
export * from "./middleware/options/ChaosHandlerOptions";
export * from "./middleware/options/ChaosStrategy";
export * from "./middleware/ChaosHandler";

export * from "./tasks/LargeFileUploadTask";
export * from "./tasks/OneDriveLargeFileUploadTask";
export * from "./tasks/PageIterator";

export * from "./Client";
export * from "./CustomAuthenticationProvider";
export * from "./GraphError";
export * from "./GraphRequest";
export * from "./IAuthProvider";
Expand Down
62 changes: 62 additions & 0 deletions src/middleware/MiddlewareFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

/**
* @module MiddlewareFactory
*/

import { AuthenticationProvider } from "../IAuthenticationProvider";

import { AuthenticationHandler } from "./AuthenticationHandler";
import { HTTPMessageHandler } from "./HTTPMessageHandler";
import { Middleware } from "./IMiddleware";
import { RedirectHandlerOptions } from "./options/RedirectHandlerOptions";
import { RetryHandlerOptions } from "./options/RetryHandlerOptions";
import { RedirectHandler } from "./RedirectHandler";
import { RetryHandler } from "./RetryHandler";
import { TelemetryHandler } from "./TelemetryHandler";

/**
* @private
* To check whether the environment is node or not
* @returns A boolean representing the environment is node or not
*/
const isNodeEnvironment = (): boolean => {
return typeof process === "object" && typeof require === "function";
};

/**
* @class
* Class containing function(s) related to the middleware pipelines.
*/
export class MiddlewareFactory {
/**
* @public
* @static
* Returns the default middleware chain an array with the middleware handlers
* @param {AuthenticationProvider} authProvider - The authentication provider instance
* @returns an array of the middleware handlers of the default middleware chain
*/
public static getDefaultMiddlewareChain(authProvider: AuthenticationProvider): Middleware[] {
const middleware: Middleware[] = [];
const authenticationHandler = new AuthenticationHandler(authProvider);
const retryHandler = new RetryHandler(new RetryHandlerOptions());
const telemetryHandler = new TelemetryHandler();
const httpMessageHandler = new HTTPMessageHandler();

middleware.push(authenticationHandler);
middleware.push(retryHandler);
if (isNodeEnvironment()) {
const redirectHandler = new RedirectHandler(new RedirectHandlerOptions());
middleware.push(redirectHandler);
}
middleware.push(telemetryHandler);
middleware.push(httpMessageHandler);

return middleware;
}
}
5 changes: 4 additions & 1 deletion src/tasks/OneDriveLargeFileUploadTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export class OneDriveLargeFileUploadTask extends LargeFileUploadTask {
}
// we choose to encode each component of the file path separately because when encoding full URI
// with encodeURI, special characters like # or % in the file name doesn't get encoded as desired
return `/me/drive/root:${path.split('/').map(p => encodeURIComponent(p)).join('/')}${encodeURIComponent(fileName)}:/createUploadSession`;
return `/me/drive/root:${path
.split("/")
.map((p) => encodeURIComponent(p))
.join("/")}${encodeURIComponent(fileName)}:/createUploadSession`;
}

/**
Expand Down