Skip to content

Commit f99d0ff

Browse files
nikithaucbaywet
andauthored
Client init with middleware array (#333)
* Passing midddleware array in client options * Tests for middleware array * Removing try catch * ifnode condition,chain middleware test * Adding missing exports * Array initialization Co-authored-by: Vincent Biret <vibiret@microsoft.com> * merging with dev * Using spread operator * Checking if middleware is not empty Co-authored-by: Vincent Biret <vibiret@microsoft.com>
1 parent bdaf597 commit f99d0ff

File tree

11 files changed

+221
-13
lines changed

11 files changed

+221
-13
lines changed

spec/core/Client.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
import { assert } from "chai";
99
import "isomorphic-fetch";
1010

11+
import { CustomAuthenticationProvider, TelemetryHandler } from "../../src";
1112
import { Client } from "../../src/Client";
1213
import { AuthProvider } from "../../src/IAuthProvider";
1314
import { ClientOptions } from "../../src/IClientOptions";
1415
import { Options } from "../../src/IOptions";
16+
import { AuthenticationHandler } from "../../src/middleware/AuthenticationHandler";
17+
import { ChaosHandler } from "../../src/middleware/ChaosHandler";
18+
import { ChaosHandlerOptions } from "../../src/middleware/options/ChaosHandlerOptions";
19+
import { ChaosStrategy } from "../../src/middleware/options/ChaosStrategy";
1520
import { DummyAuthenticationProvider } from "../DummyAuthenticationProvider";
1621
import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler";
1722

@@ -61,6 +66,41 @@ describe("Client.ts", () => {
6166
assert.equal(error.name, "InvalidMiddlewareChain");
6267
}
6368
});
69+
70+
it("Init middleware using a middleware array", async () => {
71+
const provider: AuthProvider = (done) => {
72+
done(null, "dummy_token");
73+
};
74+
const authHandler = new AuthenticationHandler(new CustomAuthenticationProvider(provider));
75+
const responseBody = "Test response body";
76+
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Testing middleware array", 200, 0, responseBody);
77+
const middlewareArray = [authHandler, new ChaosHandler(options)];
78+
const client = Client.initWithMiddleware({ middleware: middlewareArray });
79+
80+
const response = await client.api("me").get();
81+
assert.equal(response, responseBody);
82+
});
83+
84+
it("Init middleware using a chained middleware array", async () => {
85+
const provider: AuthProvider = (done) => {
86+
done(null, "dummy_token");
87+
};
88+
const authHandler = new AuthenticationHandler(new CustomAuthenticationProvider(provider));
89+
90+
const responseBody = "Test response body";
91+
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Testing chained middleware array", 200, 0, responseBody);
92+
const chaosHandler = new ChaosHandler(options);
93+
const telemetryHandler = new TelemetryHandler();
94+
95+
authHandler.setNext(telemetryHandler);
96+
telemetryHandler.setNext(chaosHandler);
97+
98+
const middlewareArray = [authHandler];
99+
const client = Client.initWithMiddleware({ middleware: middlewareArray });
100+
101+
const response = await client.api("me").get();
102+
assert.equal(response, responseBody);
103+
});
64104
});
65105

66106
describe("init", () => {

spec/core/HTTPClient.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,38 @@ describe("HTTPClient.ts", () => {
2121
assert.isDefined(httpClient["middleware"]);
2222
assert.equal(httpClient["middleware"], httpMessageHandler);
2323
});
24+
25+
it("Should create an instance and populate middleware member when passing a middleware array", () => {
26+
const client = new HTTPClient(...[httpMessageHandler]);
27+
assert.isDefined(client["middleware"]);
28+
assert.equal(client["middleware"], httpMessageHandler);
29+
});
30+
31+
it("Should throw an error if middleware is undefined", () => {
32+
try {
33+
const client = new HTTPClient();
34+
} catch (error) {
35+
assert.equal(error.name, "InvalidMiddlewareChain");
36+
}
37+
});
38+
39+
it("Should throw an error if middleware is passed as null", () => {
40+
try {
41+
const client = new HTTPClient(null);
42+
} catch (error) {
43+
assert.equal(error.name, "InvalidMiddlewareChain");
44+
}
45+
});
46+
47+
it("Should throw an error if middleware is passed as an empty array", () => {
48+
try {
49+
const client = new HTTPClient(...[]);
50+
} catch (error) {
51+
assert.equal(error.name, "InvalidMiddlewareChain");
52+
}
53+
});
2454
});
55+
2556
/* tslint:enable: no-string-literal */
2657

2758
describe("sendRequest", async () => {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* -------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
4+
* See License in the project root for license information.
5+
* -------------------------------------------------------------------------------------------
6+
*/
7+
8+
import { assert } from "chai";
9+
10+
import { AuthenticationHandler, CustomAuthenticationProvider, HTTPMessageHandler, RedirectHandler, RetryHandler, TelemetryHandler } from "../../src";
11+
import { AuthProvider } from "../../src/IAuthProvider";
12+
import { MiddlewareFactory } from "../../src/middleware/MiddlewareFactory";
13+
14+
describe("MiddlewareFactory", () => {
15+
it("Should return the default pipeline", () => {
16+
const provider: AuthProvider = (done) => {
17+
done(null, "dummy_token");
18+
};
19+
const defaultMiddleWareArray = MiddlewareFactory.getDefaultMiddlewareChain(new CustomAuthenticationProvider(provider));
20+
21+
assert.isTrue(defaultMiddleWareArray[0] instanceof AuthenticationHandler);
22+
assert.isTrue(defaultMiddleWareArray[1] instanceof RetryHandler);
23+
assert.isTrue(defaultMiddleWareArray[2] instanceof RedirectHandler);
24+
assert.isTrue(defaultMiddleWareArray[3] instanceof TelemetryHandler);
25+
assert.isTrue(defaultMiddleWareArray[4] instanceof HTTPMessageHandler);
26+
});
27+
});

src/Client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class Client {
9393
} else if (clientOptions.authProvider !== undefined) {
9494
httpClient = HTTPClientFactory.createWithAuthenticationProvider(clientOptions.authProvider);
9595
} else if (clientOptions.middleware !== undefined) {
96-
httpClient = new HTTPClient(clientOptions.middleware);
96+
httpClient = new HTTPClient(...[].concat(clientOptions.middleware));
9797
} else {
9898
const error = new Error();
9999
error.name = "InvalidMiddlewareChain";

src/HTTPClient.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,46 @@ export class HTTPClient {
2727
* @public
2828
* @constructor
2929
* Creates an instance of a HTTPClient
30-
* @param {Middleware} middleware - The first middleware of the middleware chain
30+
* @param {...Middleware} middleware - The first middleware of the middleware chain or a sequence of all the Middleware handlers
3131
*/
32-
public constructor(middleware: Middleware) {
33-
this.middleware = middleware;
32+
public constructor(...middleware: Middleware[]) {
33+
if (!middleware || !middleware.length) {
34+
const error = new Error();
35+
error.name = "InvalidMiddlewareChain";
36+
error.message = "Please provide a default middleware chain or custom middleware chain";
37+
throw error;
38+
}
39+
this.setMiddleware(...middleware);
40+
}
41+
42+
/**
43+
* @private
44+
* Processes the middleware parameter passed to set this.middleware property
45+
* @param {...Middleware} middleware - The middleware passed
46+
* @returns Nothing
47+
*/
48+
private setMiddleware(...middleware: Middleware[]): void {
49+
if (middleware.length > 1) {
50+
this.parseMiddleWareArray(middleware);
51+
} else {
52+
this.middleware = middleware[0];
53+
}
54+
}
55+
56+
/**
57+
* @private
58+
* Processes the middleware array to construct the chain
59+
* and sets this.middleware property to the first middlware handler of the array
60+
* @param {Middleware[]} middlewareArray - The array of middleware handlers
61+
* @returns Nothing
62+
*/
63+
private parseMiddleWareArray(middlewareArray: Middleware[]) {
64+
middlewareArray.forEach((element, index) => {
65+
if (index < middlewareArray.length - 1) {
66+
element.setNext(middlewareArray[index + 1]);
67+
}
68+
});
69+
this.middleware = middlewareArray[0];
3470
}
3571

3672
/**

src/HTTPClientFactory.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { TelemetryHandler } from "./middleware/TelemetryHandler";
2626
* @returns A boolean representing the environment is node or not
2727
*/
2828
const isNodeEnvironment = (): boolean => {
29-
return new Function("try {return this === global;}catch(e){return false;}")(); // tslint:disable-line: function-constructor
29+
return typeof process === "object" && typeof require === "function";
3030
};
3131

3232
/**
@@ -69,10 +69,11 @@ export class HTTPClientFactory {
6969
* @public
7070
* @static
7171
* Creates a middleware chain with the given one
72-
* @param {Middleware} middleware - The first middleware of the middleware chain
72+
* @property {...Middleware} middleware - The first middleware of the middleware chain or a sequence of all the Middleware handlers
7373
* @returns A HTTPClient instance
7474
*/
75-
public static createWithMiddleware(middleware: Middleware): HTTPClient {
76-
return new HTTPClient(middleware);
75+
public static createWithMiddleware(...middleware: Middleware[]): HTTPClient {
76+
// Middleware should not empty or undefined. This is check is present in the HTTPClient constructor.
77+
return new HTTPClient(...middleware);
7778
}
7879
}

src/IClientOptions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ import { Middleware } from "./middleware/IMiddleware";
1717
* @property {boolean} [debugLogging] - The boolean to enable/disable debug logging
1818
* @property {string} [defaultVersion] - The default version that needs to be used while making graph api request
1919
* @property {FetchOptions} [fetchOptions] - The options for fetch request
20-
* @property {Middleware} [middleware] - The first middleware of the middleware chain
20+
* @property {Middleware| Middleware[]} [middleware] - The first middleware of the middleware chain or an array of the Middleware handlers
2121
*/
2222
export interface ClientOptions {
2323
authProvider?: AuthenticationProvider;
2424
baseUrl?: string;
2525
debugLogging?: boolean;
2626
defaultVersion?: string;
2727
fetchOptions?: FetchOptions;
28-
middleware?: Middleware;
28+
middleware?: Middleware | Middleware[];
2929
}

src/browser/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,21 @@ export * from "../middleware/HTTPMessageHandler";
1313
export * from "../middleware/IMiddleware";
1414
export * from "../middleware/RetryHandler";
1515
export * from "../middleware/TelemetryHandler";
16-
16+
export * from "../middleware/MiddlewareFactory";
1717
export * from "../middleware/options/AuthenticationHandlerOptions";
1818
export * from "../middleware/options/IMiddlewareOptions";
1919
export * from "../middleware/options/RetryHandlerOptions";
2020
export * from "../middleware/options/TelemetryHandlerOptions";
21+
export * from "../middleware/options/ChaosHandlerOptions";
22+
export * from "../middleware/options/ChaosStrategy";
23+
export * from "../middleware/ChaosHandler";
2124

2225
export * from "../tasks/LargeFileUploadTask";
2326
export * from "../tasks/OneDriveLargeFileUploadTask";
2427
export * from "../tasks/PageIterator";
2528

2629
export * from "../Client";
30+
export * from "../CustomAuthenticationProvider";
2731
export * from "../GraphError";
2832
export * from "../GraphRequest";
2933
export * from "../IAuthProvider";

src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@ export * from "./middleware/IMiddleware";
1414
export * from "./middleware/RetryHandler";
1515
export * from "./middleware/RedirectHandler";
1616
export * from "./middleware/TelemetryHandler";
17-
17+
export * from "./middleware/MiddlewareFactory";
1818
export * from "./middleware/options/AuthenticationHandlerOptions";
1919
export * from "./middleware/options/IMiddlewareOptions";
2020
export * from "./middleware/options/RetryHandlerOptions";
2121
export * from "./middleware/options/RedirectHandlerOptions";
2222
export * from "./middleware/options/TelemetryHandlerOptions";
23+
export * from "./middleware/options/ChaosHandlerOptions";
24+
export * from "./middleware/options/ChaosStrategy";
25+
export * from "./middleware/ChaosHandler";
2326

2427
export * from "./tasks/LargeFileUploadTask";
2528
export * from "./tasks/OneDriveLargeFileUploadTask";
2629
export * from "./tasks/PageIterator";
2730

2831
export * from "./Client";
32+
export * from "./CustomAuthenticationProvider";
2933
export * from "./GraphError";
3034
export * from "./GraphRequest";
3135
export * from "./IAuthProvider";
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* -------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
4+
* See License in the project root for license information.
5+
* -------------------------------------------------------------------------------------------
6+
*/
7+
8+
/**
9+
* @module MiddlewareFactory
10+
*/
11+
12+
import { AuthenticationProvider } from "../IAuthenticationProvider";
13+
14+
import { AuthenticationHandler } from "./AuthenticationHandler";
15+
import { HTTPMessageHandler } from "./HTTPMessageHandler";
16+
import { Middleware } from "./IMiddleware";
17+
import { RedirectHandlerOptions } from "./options/RedirectHandlerOptions";
18+
import { RetryHandlerOptions } from "./options/RetryHandlerOptions";
19+
import { RedirectHandler } from "./RedirectHandler";
20+
import { RetryHandler } from "./RetryHandler";
21+
import { TelemetryHandler } from "./TelemetryHandler";
22+
23+
/**
24+
* @private
25+
* To check whether the environment is node or not
26+
* @returns A boolean representing the environment is node or not
27+
*/
28+
const isNodeEnvironment = (): boolean => {
29+
return typeof process === "object" && typeof require === "function";
30+
};
31+
32+
/**
33+
* @class
34+
* Class containing function(s) related to the middleware pipelines.
35+
*/
36+
export class MiddlewareFactory {
37+
/**
38+
* @public
39+
* @static
40+
* Returns the default middleware chain an array with the middleware handlers
41+
* @param {AuthenticationProvider} authProvider - The authentication provider instance
42+
* @returns an array of the middleware handlers of the default middleware chain
43+
*/
44+
public static getDefaultMiddlewareChain(authProvider: AuthenticationProvider): Middleware[] {
45+
const middleware: Middleware[] = [];
46+
const authenticationHandler = new AuthenticationHandler(authProvider);
47+
const retryHandler = new RetryHandler(new RetryHandlerOptions());
48+
const telemetryHandler = new TelemetryHandler();
49+
const httpMessageHandler = new HTTPMessageHandler();
50+
51+
middleware.push(authenticationHandler);
52+
middleware.push(retryHandler);
53+
if (isNodeEnvironment()) {
54+
const redirectHandler = new RedirectHandler(new RedirectHandlerOptions());
55+
middleware.push(redirectHandler);
56+
}
57+
middleware.push(telemetryHandler);
58+
middleware.push(httpMessageHandler);
59+
60+
return middleware;
61+
}
62+
}

0 commit comments

Comments
 (0)