From ada88f7135362cd28740319f0d2c563437a082fa Mon Sep 17 00:00:00 2001 From: Muthurathinam Date: Wed, 30 Jan 2019 16:06:37 +0530 Subject: [PATCH] Changes: 1. Implementation of middleware options at both client and request level 2. Added docs for the middleware options 3. Added middleware util to have utils for middlewares 4. Added public for constructor for more readability --- .vscode/launch.json | 14 ++ docs/CustomMiddlewareChain.md | 4 +- docs/OtherAPIs.md | 17 ++- package-lock.json | 98 ++------------ package.json | 8 +- samples/node/README.md | 2 +- ...crets.example.json => secrets.sample.json} | 0 spec/CustomHTTPHandler.ts | 15 --- spec/DummyAuthenticationProvider.ts | 20 ++- spec/DummyHTTPMessageHandler.ts | 32 +++++ spec/DummyHandlerOption.ts | 38 ++++++ spec/core/Client.ts | 10 +- spec/core/HTTPClient.ts | 68 ++++++++++ spec/core/HTTPClientFactory.ts | 6 +- .../HardCodedAuthenticationProvider.ts | 22 ++- .../middleware/AuthenticationHandler.ts | 127 ------------------ .../middleware/HTTPMessageHandler.ts | 84 ------------ spec/development/secrets.sample.ts | 2 +- spec/middleware/AuthenticationHandler.ts | 22 +++ spec/middleware/MiddlewareControl.ts | 47 +++++++ spec/middleware/MiddlewareUtil.ts | 119 ++++++++++++++++ spec/package-lock.json | 89 ++---------- spec/package.json | 3 +- src/Constants.ts | 4 + src/CustomAuthenticationProvider.ts | 3 +- src/GraphError.ts | 3 +- src/GraphRequest.ts | 48 +++++-- src/GraphRequestUtil.ts | 4 + src/GraphResponseHandler.ts | 94 +++++++------ src/HTTPClient.ts | 24 ++-- src/HTTPClientFactory.ts | 6 +- src/IClientOptions.ts | 5 +- src/IContext.ts | 7 +- src/MSALAuthenticationProvider.ts | 3 +- src/Range.ts | 3 +- src/browser/MSALAuthenticationProvider.ts | 3 +- src/browser/index.ts | 4 +- src/content/BatchRequestContent.ts | 3 +- src/content/BatchResponseContent.ts | 3 +- src/core/index.ts | 4 +- src/index.ts | 4 +- src/middleware/AuthenticationHandler.ts | 40 +++--- src/middleware/HTTPMessageHandler.ts | 7 +- src/{ => middleware}/IMiddleware.ts | 2 +- src/middleware/MiddlewareControl.ts | 50 +++++++ src/middleware/MiddlewareUtil.ts | 54 ++++++++ .../option/IMiddlewareOption.ts} | 8 +- src/tasks/LargeFileUploadTask.ts | 3 +- src/tasks/OneDriveLargeFileUploadTask.ts | 3 +- src/tasks/PageIterator.ts | 3 +- 50 files changed, 698 insertions(+), 544 deletions(-) rename samples/node/{secrets.example.json => secrets.sample.json} (100%) delete mode 100644 spec/CustomHTTPHandler.ts create mode 100644 spec/DummyHTTPMessageHandler.ts create mode 100644 spec/DummyHandlerOption.ts create mode 100644 spec/core/HTTPClient.ts delete mode 100644 spec/development/middleware/AuthenticationHandler.ts delete mode 100644 spec/development/middleware/HTTPMessageHandler.ts create mode 100644 spec/middleware/AuthenticationHandler.ts create mode 100644 spec/middleware/MiddlewareControl.ts create mode 100644 spec/middleware/MiddlewareUtil.ts rename src/{ => middleware}/IMiddleware.ts (94%) create mode 100644 src/middleware/MiddlewareControl.ts create mode 100644 src/middleware/MiddlewareUtil.ts rename src/{IMiddlewareOptions.ts => middleware/option/IMiddlewareOption.ts} (75%) diff --git a/.vscode/launch.json b/.vscode/launch.json index e06d0ea9c..57a18416a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -35,6 +35,20 @@ "${workspaceRoot}/spec/development/workload/*.js" ], "cwd": "${workspaceRoot}", + "preLaunchTask": "Run Build", + "outFiles": [], + "internalConsoleOptions": "openOnSessionStart" + }, + { + "type": "node", + "request": "launch", + "name": "Run middleware tests", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "args": [ + "${workspaceRoot}/spec/middleware/*.js" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Run Build", "outFiles": [], "internalConsoleOptions": "openOnSessionStart" } diff --git a/docs/CustomMiddlewareChain.md b/docs/CustomMiddlewareChain.md index 6188ee26d..ec80c6261 100644 --- a/docs/CustomMiddlewareChain.md +++ b/docs/CustomMiddlewareChain.md @@ -8,7 +8,7 @@ As name suggests it comes in middle of something and that is request and respons ### Implement Middlewares -Create a custom middleware pipeline by implementing the [Middleware](../src/IMiddleware.ts) interface. The following examples demonstrate how to create a custom logging middleware and how to create a custom http request a response handler. +Create a custom middleware pipeline by implementing the [Middleware](../src/middleware/IMiddleware.ts) interface. The following examples demonstrate how to create a custom logging middleware and how to create a custom http request a response handler. First middleware is passed with the context object containing request, and other middleware specific options. One has to explicitly make call to execute method of the next middleware with context object once the current middleware work is over. @@ -146,4 +146,4 @@ export class MyLoggingHandler implements Middleware { } ``` -Refer [MiddlewareOptions](../src/IMiddlewareOptions.ts) interface to know its structure. +Refer [MiddlewareOptions](../src/middleware/option/IMiddlewareOption.ts) interface to know its structure. diff --git a/docs/OtherAPIs.md b/docs/OtherAPIs.md index 7c7e848b6..b7e8ac169 100644 --- a/docs/OtherAPIs.md +++ b/docs/OtherAPIs.md @@ -74,6 +74,21 @@ try { } ``` +## MIDDLEWAREOPTION + +You can pass in the middleware options for a request through `.middlewareOption()`. This takes array of strongly typed middleware options, these middleware options should be an implementation of MiddlewareOption interface + +```typescript +try { + let res = await client.api("/me/messages").middlewareOption([ + new RetryHandlerOption(5000) + ]).get(); + console.log(res); +} catch (error) { + throw error; +} +``` + ## RESPONSETYPE To set a custom response type, use the`.responseType()` method. Refer [ResponseType.ts](./src/ResponseType.ts) for available options. @@ -85,4 +100,4 @@ try { } catch (error) { throw error; } -```` \ No newline at end of file +```` diff --git a/package-lock.json b/package-lock.json index 1a8574ab4..c29ef52e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,12 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/fetch-mock": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@types/fetch-mock/-/fetch-mock-7.2.2.tgz", - "integrity": "sha512-O828TBpGWT5c1x1/dOghDCb1+x8fNoHg0fFxEnvL6VlWuiPVgxNArIKwSsyTR8+yi14/69zGZj+uszDpWh45+A==", - "dev": true - }, "@types/isomorphic-fetch": { "version": "0.0.34", "resolved": "https://registry.npmjs.org/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.34.tgz", @@ -23,9 +17,9 @@ "dev": true }, "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "version": "10.12.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.19.tgz", + "integrity": "sha512-2NVovndCjJQj6fUUn9jCgpP4WSqr+u1SoUZMZyJkhGeBFsm6dE46l31S7lPUYt9uQ28XI+ibrJA1f5XyH5HNtA==", "dev": true }, "JSONStream": { @@ -45,9 +39,9 @@ "dev": true }, "acorn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz", - "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz", + "integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==", "dev": true }, "acorn-dynamic-import": { @@ -202,35 +196,6 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -586,12 +551,6 @@ "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", "dev": true }, - "core-js": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.3.tgz", - "integrity": "sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -743,21 +702,14 @@ } }, "detective": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", - "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", "dev": true, "requires": { - "acorn-node": "^1.3.0", + "acorn-node": "^1.6.1", "defined": "^1.0.0", "minimist": "^1.1.1" - }, - "dependencies": { - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" - } } }, "diff": { @@ -931,18 +883,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "fetch-mock": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-7.3.0.tgz", - "integrity": "sha512-KxBeS8vsADFbWPVomuwxYqOJ2obZo6CidgkypjDPeu6zl+tAJvh2GfLDmJ8u//xgBGM9iOGwOxafeqAclilH2A==", - "dev": true, - "requires": { - "babel-polyfill": "^6.26.0", - "glob-to-regexp": "^0.4.0", - "path-to-regexp": "^2.2.1", - "whatwg-url": "^6.5.0" - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1007,12 +947,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-to-regexp": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.0.tgz", - "integrity": "sha512-fyPCII4vn9Gvjq2U/oDAfP433aiE64cyP/CJjRJcpVGjqqNdioUYn9+r0cSzT1XPwmGAHuTT7iv+rQT8u/YHKQ==", - "dev": true - }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -1636,12 +1570,6 @@ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", "dev": true }, - "path-to-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", - "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", - "dev": true - }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -1789,12 +1717,6 @@ } } }, - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", diff --git a/package.json b/package.json index 3eeb70d62..e1f69da35 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,11 @@ "tslib": "^1.9.3" }, "devDependencies": { - "@types/fetch-mock": "^7.2.2", "@types/isomorphic-fetch": "0.0.34", "@types/mocha": "^5.2.5", "@types/node": "^10.12.10", "browserify": "^16.2.3", "chai": "^4.2.0", - "fetch-mock": "^7.2.5", "msal": "^0.2.4", "mocha": "^5.2.0", "typescript": "^3.1.6", @@ -33,12 +31,12 @@ "web:js": "node browserify-with-dependencies.js > lib/graph-js-sdk-web.js && uglifyjs ./lib/graph-js-sdk-web.js --output ./lib/graph-js-sdk-web.js", "core:js": "node browserify.js > lib/graph-js-sdk-core.js && uglifyjs ./lib/graph-js-sdk-core.js --output ./lib/graph-js-sdk-core.js", "build": "npm run compile && npm run web:js && npm run core:js", - "test": "mocha lib/spec/content && mocha lib/spec/core && mocha lib/spec/tasks", + "test": "mocha lib/spec/content && mocha lib/spec/core && mocha lib/spec/middleware && mocha lib/spec/tasks", "test:content": "tsc -p spec && mocha spec/content", "test:core": "tsc -p spec && mocha spec/core", + "test:middleware": "tsc -p spec && mocha spec/middleware", "test:tasks": "tsc -p spec && mocha spec/tasks", - "test:development": "tsc -p spec && mocha spec/development/middleware && mocha spec/development/workload", - "test:middleware": "tsc -p spec && mocha spec/development/middleware", + "test:development": "tsc -p spec && mocha spec/development/workload", "test:workload": "tsc -p spec && mocha spec/development/workload", "prepack": "npm install && npm run build && npm run test" }, diff --git a/samples/node/README.md b/samples/node/README.md index be868a23b..fb71b8986 100644 --- a/samples/node/README.md +++ b/samples/node/README.md @@ -4,7 +4,7 @@ You can get an access token by doing the following: -1. Rename [secrets.sample.json](./secrets.example.json) to secrets.json +1. Rename [secrets.sample.json](./secrets.sample.json) to secrets.json 2. Go to Graph Explorer. 3. Login with the account you want to use to run the node samples. 4. Open the F12 dev tools. diff --git a/samples/node/secrets.example.json b/samples/node/secrets.sample.json similarity index 100% rename from samples/node/secrets.example.json rename to samples/node/secrets.sample.json diff --git a/spec/CustomHTTPHandler.ts b/spec/CustomHTTPHandler.ts deleted file mode 100644 index 3d5d8f3ce..000000000 --- a/spec/CustomHTTPHandler.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * ------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. - * See License in the project root for license information. - * ------------------------------------------------------------------------------------------- - */ - -import { Context } from "../src/IContext"; -import { Middleware } from "../src/IMiddleware"; - -export class CustomHTTPHandler implements Middleware { - public async execute(context: Context) { - - } -} \ No newline at end of file diff --git a/spec/DummyAuthenticationProvider.ts b/spec/DummyAuthenticationProvider.ts index c084a56b7..4db654462 100644 --- a/spec/DummyAuthenticationProvider.ts +++ b/spec/DummyAuthenticationProvider.ts @@ -5,11 +5,27 @@ * ------------------------------------------------------------------------------------------- */ +/** + * @module DummyAuthenticationProvider + */ + import { AuthenticationProvider } from "../src/IAuthenticationProvider"; +/** + * @class + * @implements AuthenticationProvider + * Class representing DummyAuthenticationProvider + */ export class DummyAuthenticationProvider implements AuthenticationProvider { + + /** + * @public + * @async + * To get the access token + * @returns The promise that resolves to an access token + */ public async getAccessToken(): Promise { - let token = "DUMMY_TOKEN"; + const token = "DUMMY_TOKEN"; return Promise.resolve(token); } -} \ No newline at end of file +} diff --git a/spec/DummyHTTPMessageHandler.ts b/spec/DummyHTTPMessageHandler.ts new file mode 100644 index 000000000..3b61b5425 --- /dev/null +++ b/spec/DummyHTTPMessageHandler.ts @@ -0,0 +1,32 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module DummyHTTPMessageHandler + */ + +import { Context } from "../src/IContext"; +import { Middleware } from "../src/middleware/IMiddleware"; + +/** + * @class + * @implements Middleware + * Class representing DummyHTTPMessageHandler + */ +export class DummyHTTPMessageHandler implements Middleware { + + /** + * @public + * @async + * To execute the current middleware + * @param {Context} context - The request context object + * @returns A promise that resolves to nothing + */ + public async execute(context: Context) { + return; + } +} \ No newline at end of file diff --git a/spec/DummyHandlerOption.ts b/spec/DummyHandlerOption.ts new file mode 100644 index 000000000..da951c4bb --- /dev/null +++ b/spec/DummyHandlerOption.ts @@ -0,0 +1,38 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module DummyHandlerOption + */ + +import { MiddlewareOption } from "../src/middleware/option/IMiddlewareOption"; + +/** + * @class + * @implements MiddlewareOption + * Class for DummyHandlerOption + */ + +export class DummyHandlerOption implements MiddlewareOption { + + /** + * @public + * A member holding a dummy string + */ + public dummyString: string; + + /** + * @public + * @async + * To create an instance of DummyHandlerOption + * @param {string} dummyString - The dummy string + * @returns An instance of DummyHandlerOption + */ + public constructor(dummyString: string = "dummy") { + this.dummyString = dummyString; + } +} \ No newline at end of file diff --git a/spec/core/Client.ts b/spec/core/Client.ts index 7a4f3276e..f801898d3 100644 --- a/spec/core/Client.ts +++ b/spec/core/Client.ts @@ -7,7 +7,7 @@ import { assert } from "chai"; import { Client } from "../../src/Client"; -import { CustomHTTPHandler } from "../CustomHTTPHandler"; +import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; import { DummyAuthenticationProvider } from "../DummyAuthenticationProvider"; import { AuthProvider } from "../../src/IAuthProvider"; import { ClientOptions } from "../../src/IClientOptions"; @@ -23,17 +23,17 @@ describe("Client.ts", function () { PolyFill.init(); }); const dummyAuthProvider = new DummyAuthenticationProvider(), - customHTTPHandler = new CustomHTTPHandler(); + dummyHTTPHandler = new DummyHTTPMessageHandler(); it("Should throw an error in case if both auth provider and custom middleware is passed", () => { try { const options: ClientOptions = { authProvider: dummyAuthProvider, - middleware: customHTTPHandler + middleware: dummyHTTPHandler }; const client: Client = Client.initWithMiddleware(options); throw new Error("Something wrong with the ambiguity check"); - } catch(error) { + } catch (error) { assert.equal(error.name, "AmbiguityInInitialization"); } }); @@ -49,7 +49,7 @@ describe("Client.ts", function () { it("Should return client instance for a custom middleware chain", () => { let options: ClientOptions = { - middleware: customHTTPHandler + middleware: dummyHTTPHandler }; let client: Client = Client.initWithMiddleware(options); assert.isTrue(client instanceof Client); diff --git a/spec/core/HTTPClient.ts b/spec/core/HTTPClient.ts new file mode 100644 index 000000000..10658454e --- /dev/null +++ b/spec/core/HTTPClient.ts @@ -0,0 +1,68 @@ +/** + * ------------------------------------------------------------------------------------------- + * 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 { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; +import { HTTPClient } from "../../src/HTTPClient"; +import { Context } from "../../src/IContext"; +import { FetchOptions } from "../../src/IFetchOptions"; + +describe("HTTPClient.ts", () => { + const httpMessageHandler: DummyHTTPMessageHandler = new DummyHTTPMessageHandler(); + const httpClient: HTTPClient = new HTTPClient(httpMessageHandler); + describe("constructor", () => { + it("Should create an instance and populate middleware member", () => { + assert.isDefined(httpClient["middleware"]); + assert.equal(httpClient["middleware"], httpMessageHandler); + }); + }); + + describe("sendRequest", async () => { + it("Should throw error for invalid request options incase if the url and options are passed", async () => { + try { + const url = "dummy_url"; + const context: Context = { + request: url + }; + await httpClient.sendRequest(context); + throw new Error("Something wrong with the context validation"); + } catch (error) { + assert.equal(error.name, "InvalidRequestOptions"); + } + }); + + it("Should execute for context object with Request instance", async () => { + try { + const request: Request = new Request("dummy_url", { + method: "GET" + }); + const context: Context = { + request + }; + await httpClient.sendRequest(context); + } catch (error) { + throw error; + } + }); + + it("Should execute for context object with request uri and options", async () => { + try { + const url = "dummy_url"; + const options: FetchOptions = { + method: "GET" + } + const context: Context = { + request: url, + options + }; + await httpClient.sendRequest(context); + } catch (error) { + throw error; + } + }); + }); +}); \ No newline at end of file diff --git a/spec/core/HTTPClientFactory.ts b/spec/core/HTTPClientFactory.ts index a5a16372d..8b1266b86 100644 --- a/spec/core/HTTPClientFactory.ts +++ b/spec/core/HTTPClientFactory.ts @@ -6,7 +6,7 @@ */ import { assert } from "chai"; -import { CustomHTTPHandler } from "../CustomHTTPHandler"; +import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; import { DummyAuthenticationProvider } from "../DummyAuthenticationProvider"; import { HTTPClient } from "../../src/HTTPClient"; import { HTTPClientFactory } from "../../src/HTTPClientFactory"; @@ -15,7 +15,7 @@ describe("HTTPClientFactory.ts", function () { describe("createWithAuthenticationProvider", () => { const dummyAuthProvider = new DummyAuthenticationProvider(), - customHTTPHandler = new CustomHTTPHandler(); + dummyHTTPHandler = new DummyHTTPMessageHandler(); it("Should create an HTTPClient instance with default middleware chain", () => { let client: HTTPClient = HTTPClientFactory.createWithAuthenticationProvider(dummyAuthProvider); @@ -24,7 +24,7 @@ describe("HTTPClientFactory.ts", function () { }); it("Should create an HTTPClient with given middleware chain", () => { - let client: HTTPClient = HTTPClientFactory.createWithMiddleware(customHTTPHandler); + let client: HTTPClient = HTTPClientFactory.createWithMiddleware(dummyHTTPHandler); assert.isTrue(client instanceof HTTPClient); assert.isDefined(client["middleware"]); }); diff --git a/spec/development/HardCodedAuthenticationProvider.ts b/spec/development/HardCodedAuthenticationProvider.ts index 6eea84ecc..482499839 100644 --- a/spec/development/HardCodedAuthenticationProvider.ts +++ b/spec/development/HardCodedAuthenticationProvider.ts @@ -5,11 +5,27 @@ * ------------------------------------------------------------------------------------------- */ -import {AuthenticationProvider} from "../../src/IAuthenticationProvider"; -import {AccessToken} from "./secrets"; +/** + * @module HardCodedAuthenticationProvider + */ + +import { AuthenticationProvider } from "../../src/IAuthenticationProvider"; +import { AccessToken } from "./secrets"; +/** + * @class + * @implements AuthenticationProvider + * Class representing HardCodedAuthenticationProvider + */ export class HardCodedAuthenticationProvider implements AuthenticationProvider { - public async getAccessToken() { + + /** + * @public + * @async + * To get the access token + * @returns The promise that resolves to an access token + */ + public async getAccessToken(): Promise { return Promise.resolve(AccessToken); } } diff --git a/spec/development/middleware/AuthenticationHandler.ts b/spec/development/middleware/AuthenticationHandler.ts deleted file mode 100644 index ad2be4d07..000000000 --- a/spec/development/middleware/AuthenticationHandler.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * ------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. - * See License in the project root for license information. - * ------------------------------------------------------------------------------------------- - */ - -import { AuthenticationHandler } from "../../../src/middleware/AuthenticationHandler"; -import { assert } from "chai"; -import { DummyAuthenticationProvider } from "../../DummyAuthenticationProvider"; -import fetchMock = require("fetch-mock"); -import { HTTPMessageHandler } from "../../../src/middleware/HTTPMessageHandler"; -import { Context } from "../../../src/IContext"; - -const dummyAuthProvider = new DummyAuthenticationProvider(), - authHandler = new AuthenticationHandler(dummyAuthProvider), - httpHandler = new HTTPMessageHandler(); -authHandler.setNext(httpHandler); - -describe("AuthenticationHandler.ts", async () => { - describe("Constructor", () => { - it("Should return an AuthenticationHandler for given AuthenticationProvider", () => { - assert.isTrue(authHandler instanceof AuthenticationHandler); - assert.equal(authHandler["authProvider"], dummyAuthProvider); - }); - }); - - describe("execute", async () => { - before(() => { - fetchMock.mock("*", (url, options) => { - if (typeof options === undefined || options.headers === undefined) { - let error = new Error("Headers are empty, Please ensure to pass in the authorization header"); - error.name = "InvalidRequestOptions"; - throw error; - } - let headerType = options.headers.constructor.name, - flag: boolean = false; - if (headerType === "Headers") { - if ((options.headers as Headers).has("Authorization")) { - flag = true; - } - } else if (headerType === "Array") { - for (let i = 0, l = (options.headers as String[][]).length; i < l; i++) { - if (options.headers[i][0] === "Authorization") { - flag = true; - } - } - } else { - if ((options.headers as Record).Authorization !== undefined) { - flag = true; - } - } - if (!flag) { - let authError: Error = new Error("Authorization headers is not present, Please ensure to pass in the auth token"); - authError.name = "EmptyAuthenticationToken"; - throw authError; - } - return "200"; - }); - }); - - after(() => { - fetchMock.restore(); - }); - - it("Should set auth header for empty headers", async () => { - try { - let context: Context = { - request: "dummy_url", - options: { - method: "test" - } - }; - await authHandler.execute(context); - } catch (error) { - throw error; - } - }); - - it("Should set auth header in headers object", async () => { - try { - let context: Context = { - request: "dummy_url", - options: { - method: "test", - headers: { - version: "version" - } - } - }; - await authHandler.execute(context); - } catch (error) { - throw error; - } - }); - - it("Should set auth header in Headers instance", async () => { - try { - let context: Context = { - request: "dummy_url", - options: { - method: "test", - headers: new Headers({ version: "version" }) - } - }; - await authHandler.execute(context); - } catch (error) { - throw error; - } - }); - - it("Should set auth header in array of headers", async () => { - try { - let context: Context = { - request: "dummy_url", - options: { - method: "test", - headers: [["version", "version"]] - } - }; - await authHandler.execute(context); - } catch (error) { - throw error; - } - }) - }); -}); \ No newline at end of file diff --git a/spec/development/middleware/HTTPMessageHandler.ts b/spec/development/middleware/HTTPMessageHandler.ts deleted file mode 100644 index 5a8f69334..000000000 --- a/spec/development/middleware/HTTPMessageHandler.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * ------------------------------------------------------------------------------------------- - * 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 fetchMock = require("fetch-mock"); -import { HTTPMessageHandler } from "../../../src/middleware/HTTPMessageHandler"; -import { Context } from "../../../src/IContext"; -import "isomorphic-fetch"; - -describe("HTTPMessageHandler.ts", () => { - - let handler: HTTPMessageHandler = new HTTPMessageHandler(); - describe("execute", () => { - - let context: Context = { - request: "dummy_url1", - options: { - method: "get" - } - }; - - it("Should return proper response", async () => { - let response = new Response("url", { - status: 200, - statusText: "OK" - }) - fetchMock.once("*", response); - await handler.execute(context); - assert.isDefined(context.response); - assert.equal(context.response.status, 200); - fetchMock.restore(); - }); - - it("Should return proper error for error in response body", async () => { - - let obj = { - status: 404, - body: { - error: { - code: "DataNotFound", - message: "Unable to find the data that you are looking for", - innerError: { - "request-id": "Some random id", - date: new Date() - } - } - } - }; - fetchMock.once("*", obj); - try { - await handler.execute(context); - } catch (error) { - assert.isDefined(error); - assert.equal(error.statusCode, obj.status); - assert.equal(error.code, obj.body.error.code); - assert.equal(error.message, obj.body.error.message); - } - fetchMock.restore(); - }); - - it("Should return error for the non 2XX response without error in the response body", async () => { - let obj = { - status: 404, - body: { - someData: "someData" - } - }; - fetchMock.once("*", obj); - try { - await handler.execute(context); - } catch (error) { - assert.isDefined(error); - assert.equal(error.statusCode, obj.status); - assert.equal(error.requestId, null); - assert.equal(error.body, null); - } - fetchMock.restore(); - }); - }); -}); diff --git a/spec/development/secrets.sample.ts b/spec/development/secrets.sample.ts index bd76236f2..7010f2e87 100644 --- a/spec/development/secrets.sample.ts +++ b/spec/development/secrets.sample.ts @@ -2,6 +2,6 @@ * @file * Defines access token exporting structure * - * To use authentication based(making real requests to the graph service) testing populate this access token's value and rename this fil as secrets.ts + * To use authentication based(making real requests to the graph service) testing populate this access token's value and rename this file as secrets.ts */ export const AccessToken = ""; \ No newline at end of file diff --git a/spec/middleware/AuthenticationHandler.ts b/spec/middleware/AuthenticationHandler.ts new file mode 100644 index 000000000..c3668aadb --- /dev/null +++ b/spec/middleware/AuthenticationHandler.ts @@ -0,0 +1,22 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +import { AuthenticationHandler } from "../../src/middleware/AuthenticationHandler"; +import { assert } from "chai"; +import { DummyAuthenticationProvider } from "../DummyAuthenticationProvider"; + +const dummyAuthProvider = new DummyAuthenticationProvider(), + authHandler = new AuthenticationHandler(dummyAuthProvider); + +describe("AuthenticationHandler.ts", async () => { + describe("Constructor", () => { + it("Should return an AuthenticationHandler for given AuthenticationProvider", () => { + assert.isTrue(authHandler instanceof AuthenticationHandler); + assert.equal(authHandler["authProvider"], dummyAuthProvider); + }); + }); +}); \ No newline at end of file diff --git a/spec/middleware/MiddlewareControl.ts b/spec/middleware/MiddlewareControl.ts new file mode 100644 index 000000000..5529fc72e --- /dev/null +++ b/spec/middleware/MiddlewareControl.ts @@ -0,0 +1,47 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module MiddlewareControl + */ + +import { assert } from "chai"; +import { DummyHandlerOption} from "../DummyHandlerOption"; +import "isomorphic-fetch"; +import {MiddlewareControl} from "../../src/middleware/MiddlewareControl"; + +describe("MiddlewareControl.ts", () => { + const dummyHandlerOption = new DummyHandlerOption(); + describe("constructor", () => { + it("Should populate its middleware options map", () => { + const middlewareControl = new MiddlewareControl([dummyHandlerOption]); + assert.isDefined(middlewareControl["middlewareOptions"]); + assert.equal(middlewareControl["middlewareOptions"].size, 1); + }); + + it("Should create empty middleware options map for empty middleware options array", () => { + const middlewareControl = new MiddlewareControl([]); + assert.isDefined(middlewareControl["middlewareOptions"]); + assert.equal(middlewareControl["middlewareOptions"].size, 0); + }); + }); + + describe("getMiddlewareOption", () => { + it("Should return the middleware option for a given class name", () => { + const middlewareControl = new MiddlewareControl([dummyHandlerOption]); + const retryOption: DummyHandlerOption = (middlewareControl.getMiddlewareOption(dummyHandlerOption.constructor.name) as DummyHandlerOption); + assert.isDefined(retryOption); + assert.equal(dummyHandlerOption, retryOption); + }); + + it("Should return undefined for unknown class name", () => { + const middlewareControl = new MiddlewareControl([dummyHandlerOption]); + const retryOption = middlewareControl.getMiddlewareOption("NotAvailableHandlerOption"); + assert.isUndefined(retryOption); + }) + }); +}); \ No newline at end of file diff --git a/spec/middleware/MiddlewareUtil.ts b/spec/middleware/MiddlewareUtil.ts new file mode 100644 index 000000000..6159f5ee1 --- /dev/null +++ b/spec/middleware/MiddlewareUtil.ts @@ -0,0 +1,119 @@ +/** + * ------------------------------------------------------------------------------------------- + * 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 { FetchOptions } from "../../src/IFetchOptions"; +import { setRequestHeader, getRequestHeader } from "../../src/middleware/MiddlewareUtil"; + +describe("MiddlewareUtil.ts", () => { + describe("setRequestHeader", () => { + const key: string = "Content-Type"; + const value: string = "application/json"; + const url = "dummy_url"; + it("Should set header in request object", () => { + const request: Request = new Request(url, { + method: "test", + headers: { + version: "version", + [key]: value + } + }); + setRequestHeader(request, undefined, key, value); + assert.equal(request.headers.get(key), value); + }); + + it("Should set header for empty headers", () => { + let options: FetchOptions = { + method: "test" + }; + setRequestHeader(url, options, key, value); + assert.isDefined(options.headers); + assert.equal(options.headers[key], value); + }); + + it("Should set header in headers object", () => { + let options: FetchOptions = { + method: "test", + headers: { + version: "version" + } + }; + setRequestHeader(url, options, key, value); + assert.equal(options.headers[key], value); + }); + + it("Should set header in Headers instance", () => { + let options: FetchOptions = { + method: "test", + headers: new Headers({ version: "version" }) + }; + setRequestHeader(url, options, key, value); + assert.isDefined(options.headers); + assert.equal((options.headers as Headers).get(key), value); + }); + + it("Should set header in array of headers", () => { + let options: FetchOptions = { + method: "test", + headers: [["version", "version"]] + }; + setRequestHeader(url, options, key, value); + assert.isDefined(options.headers); + assert.equal(options.headers[1][1], value); + }); + }); + + describe("getRequestHeader", () => { + const key: string = "Content-Type"; + const value: string = "application/json"; + const url = "dummy_url"; + it("Should get header from request object", () => { + const request: Request = new Request(url, { + method: "test", + headers: { + version: "version", + [key]: value + } + }); + const headerValue: string = getRequestHeader(request, undefined, key); + assert.equal(headerValue, value); + }); + + it("Should get header from headers object", () => { + let options: FetchOptions = { + method: "test", + headers: { + version: "version", + [key]: value + } + }; + const headerValue: string = getRequestHeader(url, options, key); + assert.equal(headerValue, value); + }); + + it("Should get header from Headers instance", () => { + let options: FetchOptions = { + method: "test", + headers: new Headers({ + version: "version", + [key]: value + }) + }; + const headerValue: string = getRequestHeader(url, options, key); + assert.equal(headerValue, value); + }); + + it("Should get header from array of headers", () => { + let options: FetchOptions = { + method: "test", + headers: [["version", "version"], [key, value]] + }; + const headerValue: string = getRequestHeader(url, options, key); + assert.equal(headerValue, value); + }); + }); +}); \ No newline at end of file diff --git a/spec/package-lock.json b/spec/package-lock.json index 2daa2edc9..7d885ba6f 100644 --- a/spec/package-lock.json +++ b/spec/package-lock.json @@ -13,9 +13,9 @@ } }, "@microsoft/microsoft-graph-types": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.5.0.tgz", - "integrity": "sha512-eit9LMlhHLyzCByATGL8d7izWNRVBlzrPMfXqy2a9qIVUUNIGxyqkGJNrpEh3EVDXW2r+/ASsBAqzHkm8zWJiQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.6.0.tgz", + "integrity": "sha512-NAkRxtKQWj+ju0Ra36ZQDjVAoJDYvP2eHq27Fn0yAkSa7CN7EaYy7YuuSdo73Wgu2p3XJp3CYze08ub31DXbEA==", "dev": true }, "@microsoft/microsoft-graph-types-beta": { @@ -29,12 +29,6 @@ "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", "dev": true }, - "@types/fetch-mock": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@types/fetch-mock/-/fetch-mock-7.2.2.tgz", - "integrity": "sha512-O828TBpGWT5c1x1/dOghDCb1+x8fNoHg0fFxEnvL6VlWuiPVgxNArIKwSsyTR8+yi14/69zGZj+uszDpWh45+A==", - "dev": true - }, "@types/form-data": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", @@ -57,9 +51,9 @@ "dev": true }, "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "version": "10.12.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.19.tgz", + "integrity": "sha512-2NVovndCjJQj6fUUn9jCgpP4WSqr+u1SoUZMZyJkhGeBFsm6dE46l31S7lPUYt9uQ28XI+ibrJA1f5XyH5HNtA==", "dev": true }, "abab": { @@ -85,9 +79,9 @@ }, "dependencies": { "acorn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz", - "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz", + "integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==", "dev": true } } @@ -161,35 +155,6 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -246,12 +211,6 @@ "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==", "dev": true }, - "core-js": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.3.tgz", - "integrity": "sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -398,18 +357,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "fetch-mock": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-7.3.0.tgz", - "integrity": "sha512-KxBeS8vsADFbWPVomuwxYqOJ2obZo6CidgkypjDPeu6zl+tAJvh2GfLDmJ8u//xgBGM9iOGwOxafeqAclilH2A==", - "dev": true, - "requires": { - "babel-polyfill": "^6.26.0", - "glob-to-regexp": "^0.4.0", - "path-to-regexp": "^2.2.1", - "whatwg-url": "^6.5.0" - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -442,12 +389,6 @@ "assert-plus": "^1.0.0" } }, - "glob-to-regexp": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.0.tgz", - "integrity": "sha512-fyPCII4vn9Gvjq2U/oDAfP433aiE64cyP/CJjRJcpVGjqqNdioUYn9+r0cSzT1XPwmGAHuTT7iv+rQT8u/YHKQ==", - "dev": true - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -685,12 +626,6 @@ "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "dev": true }, - "path-to-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", - "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", - "dev": true - }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -733,12 +668,6 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", diff --git a/spec/package.json b/spec/package.json index e601f65ce..c90b46322 100644 --- a/spec/package.json +++ b/spec/package.json @@ -4,13 +4,12 @@ "@microsoft/microsoft-graph-types": "^1.5.0", "@microsoft/microsoft-graph-types-beta": "microsoftgraph/msgraph-typescript-typings#beta", "@types/chai": "^4.1.7", - "@types/fetch-mock": "^7.2.2", "@types/form-data": "^2.2.1", "@types/isomorphic-fetch": "0.0.34", "@types/mocha": "^5.2.5", + "@types/node": "^10.12.19", "chai": "^4.2.0", "es6-promise": "^4.2.5", - "fetch-mock": "^7.2.5", "form-data": "^2.3.3", "isomorphic-fetch": "^2.2.1", "msal": "^0.2.3", diff --git a/src/Constants.ts b/src/Constants.ts index c08f9e5af..447035752 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -5,6 +5,10 @@ * ------------------------------------------------------------------------------------------- */ +/** + * @module Constants + */ + /** * @constant * A Default API endpoint version for a request diff --git a/src/CustomAuthenticationProvider.ts b/src/CustomAuthenticationProvider.ts index 2f35b1c31..4dbf68eed 100644 --- a/src/CustomAuthenticationProvider.ts +++ b/src/CustomAuthenticationProvider.ts @@ -26,12 +26,13 @@ export class CustomAuthenticationProvider implements AuthenticationProvider { private provider: AuthProvider; /** + * @public * @constructor * Creates an instance of CustomAuthenticationProvider * @param {AuthProviderCallback} provider - An authProvider function * @returns An instance of CustomAuthenticationProvider */ - constructor(provider: AuthProvider) { + public constructor(provider: AuthProvider) { this.provider = provider; } diff --git a/src/GraphError.ts b/src/GraphError.ts index 499134f3d..e4506f353 100644 --- a/src/GraphError.ts +++ b/src/GraphError.ts @@ -56,12 +56,13 @@ export class GraphError { public body: any; /** + * @public * @constructor * Creates an instance of GraphError * @param {number} [statusCode = -1] - The status code of the error * @returns An instance of GraphError */ - constructor(statusCode: number = -1) { + public constructor(statusCode: number = -1) { let self = this; self.statusCode = statusCode; self.code = null; diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index deea5aed6..64f711f73 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -19,6 +19,8 @@ import { ClientOptions } from "./IClientOptions"; import { Context } from "./IContext"; import { GraphRequestCallback } from "./IGraphRequestCallback"; import { FetchOptions } from "./IFetchOptions"; +import { MiddlewareOption } from "./middleware/option/IMiddlewareOption"; +import { MiddlewareControl } from "./middleware/MiddlewareControl"; import { RequestMethod } from "./RequestMethod"; import { ResponseType } from "./ResponseType"; @@ -88,6 +90,12 @@ export class GraphRequest { */ private _options: FetchOptions; + /** + * @private + * A member to hold the array of middleware options for a request + */ + private _middlewareOptions: MiddlewareOption[]; + /** * @private * A member to hold custom response type for a request @@ -95,12 +103,14 @@ export class GraphRequest { private _responseType: ResponseType; /** + * @public + * @constructor * Creates an instance of GraphRequest * @param {HTTPClient} httpClient - The HTTPClient instance * @param {ClientOptions} config - The options for making request * @param {string} path - A path string */ - constructor(httpClient: HTTPClient, config: ClientOptions, path: string) { + public constructor(httpClient: HTTPClient, config: ClientOptions, path: string) { let self = this; self.httpClient = httpClient; self.config = config; @@ -110,8 +120,9 @@ export class GraphRequest { oDataQueryParams: {}, otherURLQueryParams: {} }; - self._options = {}; self._headers = {}; + self._options = {}; + self._middlewareOptions = []; self.parsePath(path); } @@ -130,7 +141,7 @@ export class GraphRequest { // Find where the host ends let endOfHostStrPos = path.indexOf("/"); - if(endOfHostStrPos !== -1) { + if (endOfHostStrPos !== -1) { // Parse out the host self.urlComponents.host = "https://" + path.substring(0, endOfHostStrPos); // Strip the host from path @@ -139,7 +150,7 @@ export class GraphRequest { // Remove the following version let endOfVersionStrPos = path.indexOf("/"); - if(endOfVersionStrPos !== -1) { + if (endOfVersionStrPos !== -1) { // Parse out the version self.urlComponents.version = path.substring(0, endOfVersionStrPos); // Strip version from path @@ -228,6 +239,18 @@ export class GraphRequest { return self; } + /** + * @public + * Sets the middleware options for a request + * @param {MiddlewareOption[]} options - The array of middleware options + * @returns The same GraphRequest instance that is being called with + */ + public middlewareOptions(options: MiddlewareOption[]): GraphRequest { + let self = this; + self._middlewareOptions = options; + return self; + } + /** * @public * Sets the api endpoint version for a request @@ -493,11 +516,15 @@ export class GraphRequest { private async send(request: RequestInfo, options: FetchOptions, callback?: GraphRequestCallback): Promise { let self = this, rawResponse: Response, - middlewareOptions = Object.assign({}, self.config.middlewareOptions); + middlewareControl = new MiddlewareControl(self._middlewareOptions); self.updateRequestOptions(options); try { - let context: Context = await self.httpClient.sendRequest(request, options, middlewareOptions); - rawResponse = context.response; + let context: Context = await self.httpClient.sendRequest({ + request, + options, + middlewareControl + }), + rawResponse = context.response; let response: any = await GraphResponseHandler.getResponse(rawResponse, self._responseType, callback); return response; } catch (error) { @@ -582,10 +609,7 @@ export class GraphRequest { url = self.buildFullUrl(), options: FetchOptions = { method: RequestMethod.PUT, - body: serializeContent(content), - headers: { - "Content-Type": "application/octet-stream" - } + body: serializeContent(content) }; try { let response = await self.send(url, options, callback); @@ -712,4 +736,4 @@ export class GraphRequest { throw error; } } -} \ No newline at end of file +} diff --git a/src/GraphRequestUtil.ts b/src/GraphRequestUtil.ts index f933703cf..d8280207c 100644 --- a/src/GraphRequestUtil.ts +++ b/src/GraphRequestUtil.ts @@ -5,6 +5,10 @@ * ------------------------------------------------------------------------------------------- */ +/** + * @module GraphRequestUtil + */ + /** * To hold list of OData query params */ diff --git a/src/GraphResponseHandler.ts b/src/GraphResponseHandler.ts index fbb1196e8..3ec510288 100644 --- a/src/GraphResponseHandler.ts +++ b/src/GraphResponseHandler.ts @@ -77,56 +77,64 @@ export class GraphResponseHandler { * @returns A promise that resolves to the converted response content */ private static async convertResponse(rawResponse: Response, responseType?: ResponseType): Promise { - if(responseType === ResponseType.RAW) { + if (responseType === ResponseType.RAW) { return Promise.resolve(rawResponse); } if (rawResponse.status === 204) { //NO CONTENT return Promise.resolve(); } let responseValue: any; - switch (responseType) { - case ResponseType.ARRAYBUFFER: - responseValue = await rawResponse.arrayBuffer(); - break; - case ResponseType.BLOB: - responseValue = await rawResponse.blob(); - break; - case ResponseType.DOCUMENT: - responseValue = await GraphResponseHandler.parseDocumentResponse(rawResponse, DocumentType.TEXT_XML); - break; - case ResponseType.JSON: - responseValue = await rawResponse.json(); - break; - case ResponseType.STREAM: - responseValue = await Promise.resolve(rawResponse.body); - break; - case ResponseType.TEXT: - responseValue = await rawResponse.text(); - break; - default: - let contentType = rawResponse.headers.get("Content-type"); - if (contentType !== null) { - let mimeType = contentType.split(";")[0]; - if (GraphResponseHandler.DocumentTypes.includes(mimeType)) { - responseValue = await GraphResponseHandler.parseDocumentResponse(rawResponse, mimeType as DocumentType); + try { + switch (responseType) { + case ResponseType.ARRAYBUFFER: + responseValue = await rawResponse.arrayBuffer(); + break; + case ResponseType.BLOB: + responseValue = await rawResponse.blob(); + break; + case ResponseType.DOCUMENT: + responseValue = await GraphResponseHandler.parseDocumentResponse(rawResponse, DocumentType.TEXT_XML); + break; + case ResponseType.JSON: + responseValue = await rawResponse.json(); + break; + case ResponseType.STREAM: + responseValue = await Promise.resolve(rawResponse.body); + break; + case ResponseType.TEXT: + responseValue = await rawResponse.text(); + break; + default: + let contentType = rawResponse.headers.get("Content-type"); + if (contentType !== null) { + let mimeType = contentType.split(";")[0]; + if (GraphResponseHandler.DocumentTypes.includes(mimeType)) { + responseValue = await GraphResponseHandler.parseDocumentResponse(rawResponse, mimeType as DocumentType); + } else { + responseValue = await rawResponse.json(); + } } else { - responseValue = await rawResponse.json(); + /** + * RFC specification {@link https://tools.ietf.org/html/rfc7231#section-3.1.1.5} says: + * A sender that generates a message containing a payload body SHOULD + * generate a Content-Type header field in that message unless the + * intended media type of the enclosed representation is unknown to the + * sender. If a Content-Type header field is not present, the recipient + * MAY either assume a media type of "application/octet-stream" + * ([RFC2046], Section 4.5.1) or examine the data to determine its type. + * + * So assuming it as a stream type so returning the body. + */ + responseValue = Promise.resolve(rawResponse.body); } - } else { - /** - * RFC specification {@link https://tools.ietf.org/html/rfc7231#section-3.1.1.5} says: - * A sender that generates a message containing a payload body SHOULD - * generate a Content-Type header field in that message unless the - * intended media type of the enclosed representation is unknown to the - * sender. If a Content-Type header field is not present, the recipient - * MAY either assume a media type of "application/octet-stream" - * ([RFC2046], Section 4.5.1) or examine the data to determine its type. - * - * So assuming it as a stream type so returning the body. - */ - responseValue = Promise.resolve(rawResponse.body); - } - break; + break; + } + } catch (error) { + if (typeof responseType !== "undefined" && responseType !== ResponseType.JSON) { + responseValue = await rawResponse.json(); + } else { + throw error; + } } return responseValue; } @@ -157,4 +165,4 @@ export class GraphResponseHandler { throw error; } } -} \ No newline at end of file +} diff --git a/src/HTTPClient.ts b/src/HTTPClient.ts index ec7721909..4cf9dfa47 100644 --- a/src/HTTPClient.ts +++ b/src/HTTPClient.ts @@ -10,9 +10,7 @@ */ import { Context } from "./IContext"; -import { FetchOptions } from "./IFetchOptions"; -import { Middleware } from "./IMiddleware"; -import { MiddlewareOptions } from "./IMiddlewareOptions"; +import { Middleware } from "./middleware/IMiddleware"; /** * @class @@ -27,11 +25,12 @@ export class HTTPClient { private middleware: Middleware; /** + * @public * @constructor * Creates an instance of a HTTPClient * @param {Middleware} middleware - The first middleware of the middleware chain */ - constructor(middleware: Middleware) { + public constructor(middleware: Middleware) { this.middleware = middleware; } @@ -39,18 +38,17 @@ export class HTTPClient { * @public * @async * To send the request through the middleware chain - * @param {RequestInfo} request - The request url string or the Request instance - * @param {FetchOptions} options - The options of a request - * @param {MiddlewareOptions} middlewareOptions - The options of a middleware chain + * @param {Context} context - The context of a request * @returns A promise that resolves to the Context */ - public async sendRequest(request: RequestInfo, options: FetchOptions, middlewareOptions: MiddlewareOptions): Promise { + public async sendRequest(context: Context): Promise { try { - let context: Context = { - request, - options, - middlewareOptions - }; + if (!(context.request instanceof Request) && context.options === undefined) { + const error = new Error(); + error.name = "InvalidRequestOptions"; + error.message = "Unable to execute the middleware, Please provide valid options for a request"; + throw error; + } await this.middleware.execute(context); return context; } catch (error) { diff --git a/src/HTTPClientFactory.ts b/src/HTTPClientFactory.ts index c840e2bb6..401c74f2e 100644 --- a/src/HTTPClientFactory.ts +++ b/src/HTTPClientFactory.ts @@ -13,7 +13,7 @@ import { AuthenticationHandler } from "./middleware/AuthenticationHandler"; import { HTTPMessageHandler } from "./middleware/HTTPMessageHandler"; import { HTTPClient } from "./HTTPClient"; import { AuthenticationProvider } from "./IAuthenticationProvider"; -import { Middleware } from "./IMiddleware"; +import { Middleware } from "./middleware/IMiddleware"; /** * @class @@ -29,8 +29,8 @@ export class HTTPClientFactory { * @returns A HTTPClient instance */ public static createWithAuthenticationProvider(authProvider: AuthenticationProvider): HTTPClient { - let authenticationHandler = new AuthenticationHandler(authProvider); - let httpMessageHandler = new HTTPMessageHandler(); + const authenticationHandler = new AuthenticationHandler(authProvider); + const httpMessageHandler = new HTTPMessageHandler(); authenticationHandler.setNext(httpMessageHandler); return HTTPClientFactory.createWithMiddleware(authenticationHandler); } diff --git a/src/IClientOptions.ts b/src/IClientOptions.ts index ca0897812..c2b547918 100644 --- a/src/IClientOptions.ts +++ b/src/IClientOptions.ts @@ -7,8 +7,7 @@ import { AuthenticationProvider } from "./IAuthenticationProvider"; import { FetchOptions } from "./IFetchOptions"; -import { Middleware } from "./IMiddleware"; -import { MiddlewareOptions } from "./IMiddlewareOptions"; +import { Middleware } from "./middleware/IMiddleware"; /** * @interface @@ -19,7 +18,6 @@ import { MiddlewareOptions } from "./IMiddlewareOptions"; * @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 {Function} [middleware] - The first middleware of the middleware chain -* @property {MiddlewareOptions} [middlewareOptions] - The options for middleware */ export interface ClientOptions { authProvider?: AuthenticationProvider; @@ -28,5 +26,4 @@ export interface ClientOptions { defaultVersion?: string; fetchOptions?: FetchOptions; middleware?: Middleware; - middlewareOptions?: MiddlewareOptions; } \ No newline at end of file diff --git a/src/IContext.ts b/src/IContext.ts index cc3305fe9..78e9aeffc 100644 --- a/src/IContext.ts +++ b/src/IContext.ts @@ -6,18 +6,19 @@ */ import { FetchOptions } from "./IFetchOptions"; -import { MiddlewareOptions } from "./IMiddlewareOptions"; +import { MiddlewareControl } from "./middleware/MiddlewareControl"; /** * @interface * @property {RequestInfo} request - The request url string or the Request instance * @property {FetchOptions} [options] - The options for the request * @property {Response} [response] - The response content - * @property {MiddlewareOptions} [middlewareOptions] - The options for the middleware chain + * @property {MiddlewareControl} [middlewareControl] - The options for the middleware chain */ + export interface Context { request: RequestInfo; options?: FetchOptions; response?: Response; - middlewareOptions?: MiddlewareOptions; + middlewareControl?: MiddlewareControl; } \ No newline at end of file diff --git a/src/MSALAuthenticationProvider.ts b/src/MSALAuthenticationProvider.ts index 597ad958c..89863fdc0 100644 --- a/src/MSALAuthenticationProvider.ts +++ b/src/MSALAuthenticationProvider.ts @@ -38,6 +38,7 @@ export class MSALAuthenticationProvider implements AuthenticationProvider { private userAgentApplication: UserAgentApplication; /** + * @public * @constructor * Creates an instance of MSALAuthenticationProvider * @param {string} clientId - The clientId value of an application @@ -45,7 +46,7 @@ export class MSALAuthenticationProvider implements AuthenticationProvider { * @param {any} [options] - An options object for MSAL initialization * @returns An instance of MSALAuthenticationProvider */ - constructor(clientId: string, scopes: string[], options?: any) { + public constructor(clientId: string, scopes: string[], options?: any) { const callback = (errorDesc, token, error, tokenType) => { }; diff --git a/src/Range.ts b/src/Range.ts index 20b2cd06b..29612fd21 100644 --- a/src/Range.ts +++ b/src/Range.ts @@ -28,13 +28,14 @@ export class Range { public maxValue: number /** + * @public * @constructor * Creates a range for given min and max values * @param {number} [minVal = -1] - The minimum value. * @param {number} [maxVal = -1] - The maximum value. * @returns An instance of a Range */ - constructor(minVal: number = -1, maxVal: number = -1) { + public constructor(minVal: number = -1, maxVal: number = -1) { let self = this; self.minValue = minVal; self.maxValue = maxVal; diff --git a/src/browser/MSALAuthenticationProvider.ts b/src/browser/MSALAuthenticationProvider.ts index 6f1209575..cc4e87d63 100644 --- a/src/browser/MSALAuthenticationProvider.ts +++ b/src/browser/MSALAuthenticationProvider.ts @@ -43,6 +43,7 @@ export class MSALAuthenticationProvider implements AuthenticationProvider { private userAgentApplication: any; /** + * @public * @constructor * Creates an instance of MSALAuthenticationProvider * @param {string} clientId - The clientId value of an application @@ -50,7 +51,7 @@ export class MSALAuthenticationProvider implements AuthenticationProvider { * @param {any} [options] - An options object for MSAL initialization * @returns An instance of MSALAuthenticationProvider */ - constructor(clientId: string, scopes: string[], options?: any) { + public constructor(clientId: string, scopes: string[], options?: any) { const callback = (errorDesc, token, error, tokenType) => { }; diff --git a/src/browser/index.ts b/src/browser/index.ts index e5a9e3714..5373425cf 100644 --- a/src/browser/index.ts +++ b/src/browser/index.ts @@ -10,6 +10,9 @@ export * from "../content/BatchResponseContent"; export * from "../middleware/AuthenticationHandler"; export * from "../middleware/HTTPMessageHandler"; +export * from "../middleware/IMiddleware"; + +export * from "../middleware/option/IMiddlewareOption"; export * from "../tasks/OneDriveLargeFileUploadTask"; export * from "../tasks/PageIterator"; @@ -24,7 +27,6 @@ export * from "../IClientOptions"; export * from "../IContext"; export * from "../IFetchOptions"; export * from "../IGraphRequestCallback"; -export * from "../IMiddleware"; export * from "../IOptions"; export * from "./MSALAuthenticationProvider"; export * from "../ResponseType"; \ No newline at end of file diff --git a/src/content/BatchRequestContent.ts b/src/content/BatchRequestContent.ts index 7a64f39c1..3f2a0b35b 100644 --- a/src/content/BatchRequestContent.ts +++ b/src/content/BatchRequestContent.ts @@ -95,12 +95,13 @@ export class BatchRequestContent { requests: Map; /** + * @public * @constructor * Constructs a BatchRequestContent instance * @param {BatchRequestStep[]} [requests] - Array of requests value * @returns An instance of a BatchRequestContent */ - constructor(requests?: BatchRequestStep[]) { + public constructor(requests?: BatchRequestStep[]) { let self = this; self.requests = new Map(); if (typeof requests !== "undefined") { diff --git a/src/content/BatchResponseContent.ts b/src/content/BatchResponseContent.ts index a31e0c712..6199d1401 100644 --- a/src/content/BatchResponseContent.ts +++ b/src/content/BatchResponseContent.ts @@ -47,12 +47,13 @@ export class BatchResponseContent { /** + * @public * @constructor * Creates the BatchResponseContent instance * @param {BatchResponseBody} response - The response body returned for batch request from server * @returns An instance of a BatchResponseContent */ - constructor(response: BatchResponseBody) { + public constructor(response: BatchResponseBody) { let self = this; self.responses = new Map(); self.update(response); diff --git a/src/core/index.ts b/src/core/index.ts index 668482099..b8850e9ce 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -10,6 +10,9 @@ export * from "../content/BatchResponseContent"; export * from "../middleware/AuthenticationHandler"; export * from "../middleware/HTTPMessageHandler"; +export * from "../middleware/IMiddleware"; + +export * from "../middleware/option/IMiddlewareOption"; export * from "../tasks/OneDriveLargeFileUploadTask"; export * from "../tasks/PageIterator"; @@ -24,6 +27,5 @@ export * from "../IClientOptions"; export * from "../IContext"; export * from "../IFetchOptions"; export * from "../IGraphRequestCallback"; -export * from "../IMiddleware"; export * from "../IOptions"; export * from "../ResponseType"; diff --git a/src/index.ts b/src/index.ts index 6d4e50093..f3d951f2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,9 @@ export * from "./content/BatchResponseContent"; export * from "./middleware/AuthenticationHandler"; export * from "./middleware/HTTPMessageHandler"; +export * from "./middleware/IMiddleware"; + +export * from "./middleware/option/IMiddlewareOption"; export * from "./tasks/OneDriveLargeFileUploadTask"; export * from "./tasks/PageIterator"; @@ -30,6 +33,5 @@ export * from "./IClientOptions"; export * from "./IContext"; export * from "./IFetchOptions"; export * from "./IGraphRequestCallback"; -export * from "./IMiddleware"; export * from "./IOptions"; export * from "./ResponseType"; diff --git a/src/middleware/AuthenticationHandler.ts b/src/middleware/AuthenticationHandler.ts index f43fe6d6f..0ea56f100 100644 --- a/src/middleware/AuthenticationHandler.ts +++ b/src/middleware/AuthenticationHandler.ts @@ -9,17 +9,24 @@ * @module AuthenticationHandler */ -import { Middleware } from "../IMiddleware"; +import { Middleware } from "./IMiddleware"; import { AuthenticationProvider } from "../IAuthenticationProvider"; import { Context } from "../IContext"; +import { setRequestHeader } from "./MiddlewareUtil"; /** * @class * Class representing AuthenticationHandler - * @extends Middleware + * @implements Middleware */ export class AuthenticationHandler implements Middleware { + /** + * @private + * A member representing the authorization header name + */ + private static AUTHORIZATION_HEADER: string = "Authorization"; + /** * @private * A member to hold an AuthenticationProvider instance @@ -33,11 +40,12 @@ export class AuthenticationHandler implements Middleware { private nextMiddleware: Middleware; /** + * @public * @constructor * Creates an instance of AuthenticationHandler * @param {AuthenticationProvider} authProvider - The authentication provider for the authentication handler */ - constructor(authProvider: AuthenticationProvider) { + public constructor(authProvider: AuthenticationProvider) { this.authProvider = authProvider; } @@ -46,31 +54,13 @@ export class AuthenticationHandler implements Middleware { * @async * To execute the current middleware * @param {context} context - The context object of the request - * @returns A Promise that resolves to Nothing + * @returns A Promise that resolves to nothing */ public async execute(context: Context): Promise { try { - let token = await this.authProvider.getAccessToken(); - let bearerKey = `Bearer ${token}`; - if (context.request.constructor.name === "Request") { - (context.request).headers.set("Authorization", bearerKey); - } else { - let options = context.options; - if (options.headers === undefined) { - options.headers = { - "Authorization": bearerKey - }; - } else { - let headerType = options.headers.constructor.name; - if (headerType === "Headers") { - (options.headers).set("Authorization", bearerKey); - } else if (headerType === "Array") { - (options.headers).push(["Authorization", bearerKey]); - } else { - Object.assign(options.headers, { Authorization: bearerKey }); - } - } - } + const token = await this.authProvider.getAccessToken(); + const bearerKey = `Bearer ${token}`; + setRequestHeader(context.request, context.options, AuthenticationHandler.AUTHORIZATION_HEADER, bearerKey); return await this.nextMiddleware.execute(context); } catch (error) { throw error; diff --git a/src/middleware/HTTPMessageHandler.ts b/src/middleware/HTTPMessageHandler.ts index 32a7978e2..91116307c 100644 --- a/src/middleware/HTTPMessageHandler.ts +++ b/src/middleware/HTTPMessageHandler.ts @@ -10,22 +10,21 @@ */ import { Context } from "../IContext"; -import { Middleware } from "../IMiddleware"; +import { Middleware } from "./IMiddleware"; /** * @class * Class for HTTPMessageHandler - * @extends Middleware + * @implements Middleware */ export class HTTPMessageHandler implements Middleware { - /** * @public * @async * To execute the current middleware * @param {Context} context - The request context object - * @returns A promise that resolves to Nothing + * @returns A promise that resolves to nothing */ public async execute(context: Context): Promise { try { diff --git a/src/IMiddleware.ts b/src/middleware/IMiddleware.ts similarity index 94% rename from src/IMiddleware.ts rename to src/middleware/IMiddleware.ts index ec09c38a8..fd739549c 100644 --- a/src/IMiddleware.ts +++ b/src/middleware/IMiddleware.ts @@ -5,7 +5,7 @@ * ------------------------------------------------------------------------------------------- */ -import { Context } from "./IContext"; +import { Context } from "../IContext"; /** * @interface diff --git a/src/middleware/MiddlewareControl.ts b/src/middleware/MiddlewareControl.ts new file mode 100644 index 000000000..8770d2e5b --- /dev/null +++ b/src/middleware/MiddlewareControl.ts @@ -0,0 +1,50 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module MiddlewareControl + */ + +import { MiddlewareOption } from "./option/IMiddlewareOption"; + +/** + * @class + * Class representing MiddlewareControl + */ +export class MiddlewareControl { + + /** + * @private + * A member holding map of MiddlewareOption + */ + private middlewareOptions: Map; + + /** + * @public + * @constructor + * Creates an instance of MiddlewareControl + * @param {MiddlewareOption[]} middlewareOptions - The array of middlewareOptions + * @returns The instance of MiddlewareControl + */ + public constructor(middlewareOptions: MiddlewareOption[]) { + this.middlewareOptions = new Map(); + for (let option of middlewareOptions) { + let name = option.constructor.name; + this.middlewareOptions.set(name, option); + } + } + + /** + * @public + * To get the middleware option using the class name of the option + * @param {string} name - The class name of the strongly types option class + * @returns The middleware option + */ + public getMiddlewareOption(name: string): MiddlewareOption { + return this.middlewareOptions.get(name); + } +} \ No newline at end of file diff --git a/src/middleware/MiddlewareUtil.ts b/src/middleware/MiddlewareUtil.ts new file mode 100644 index 000000000..671087817 --- /dev/null +++ b/src/middleware/MiddlewareUtil.ts @@ -0,0 +1,54 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module MiddlewareUtil + */ + +import { FetchOptions } from '../IFetchOptions'; + +export const getRequestHeader = (request: RequestInfo, options: FetchOptions | undefined, key: string): string | null => { + let value: string = null; + if (request instanceof Request) { + value = (request as Request).headers.get(key); + } else if (typeof options !== "undefined" && options.headers !== undefined) { + if (options.headers instanceof Headers) { + value = (options.headers as Headers).get(key); + } else if (options.headers instanceof Array) { + const headers = (options.headers as string[][]); + for (let i = 0, l = headers.length; i < l; i++) { + if (headers[i][0] === key) { + value = headers[i][1]; + break; + } + } + } else if (options.headers[key] !== undefined) { + value = options.headers[key]; + } + } + return value; +} + +export const setRequestHeader = (request: RequestInfo, options: FetchOptions | undefined, key: string, value: string): void => { + if (request instanceof Request) { + (request as Request).headers.set(key, value); + } else if (typeof options !== "undefined") { + if (options.headers === undefined) { + options.headers = { + [key]: value + }; + } else { + if (options.headers instanceof Headers) { + (options.headers as Headers).set(key, value); + } else if (options.headers instanceof Array) { + (options.headers as string[][]).push([key, value]); + } else { + Object.assign(options.headers, { [key]: value }); + } + } + } +} \ No newline at end of file diff --git a/src/IMiddlewareOptions.ts b/src/middleware/option/IMiddlewareOption.ts similarity index 75% rename from src/IMiddlewareOptions.ts rename to src/middleware/option/IMiddlewareOption.ts index 5475427f7..ed65b054b 100644 --- a/src/IMiddlewareOptions.ts +++ b/src/middleware/option/IMiddlewareOption.ts @@ -8,8 +8,8 @@ /** * @interface * Signature representing the middleware options - * @property {[key: string]: any} - The key value pair for request options */ -export interface MiddlewareOptions { - [key: string]: any -} \ No newline at end of file + +export interface MiddlewareOption { + +} diff --git a/src/tasks/LargeFileUploadTask.ts b/src/tasks/LargeFileUploadTask.ts index be6fd1c6d..85f87cc62 100644 --- a/src/tasks/LargeFileUploadTask.ts +++ b/src/tasks/LargeFileUploadTask.ts @@ -99,6 +99,7 @@ export class LargeFileUploadTask { private DEFAULT_FILE_SIZE: number = 5 * 1024 * 1024; /** + * @public * @constructor * Constructs a LargeFileUploadTask * @param {Client} client - The GraphClient instance @@ -107,7 +108,7 @@ export class LargeFileUploadTask { * @param {LargeFileUploadTaskOptions} options - The upload task options * @returns An instance of LargeFileUploadTask */ - constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions) { + public constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions) { let self = this; self.client = client; self.file = file; diff --git a/src/tasks/OneDriveLargeFileUploadTask.ts b/src/tasks/OneDriveLargeFileUploadTask.ts index 9031a5009..655978d6f 100644 --- a/src/tasks/OneDriveLargeFileUploadTask.ts +++ b/src/tasks/OneDriveLargeFileUploadTask.ts @@ -40,6 +40,7 @@ export class OneDriveLargeFileUploadTask extends LargeFileUploadTask { private static DEFAULT_UPLOAD_PATH: string = "/"; /** + * @public * @constructor * Constructs a OneDriveLargeFileUploadTask * @param {Client} client - The GraphClient instance @@ -48,7 +49,7 @@ export class OneDriveLargeFileUploadTask extends LargeFileUploadTask { * @param {LargeFileUploadTaskOptions} options - The upload task options * @returns An instance of OneDriveLargeFileUploadTask */ - constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions) { + public constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions) { super(client, file, uploadSession, options); } diff --git a/src/tasks/PageIterator.ts b/src/tasks/PageIterator.ts index b6392c054..f92fb2b65 100644 --- a/src/tasks/PageIterator.ts +++ b/src/tasks/PageIterator.ts @@ -70,6 +70,7 @@ export class PageIterator { private callback: PageIteratorCallback; /** + * @public * @constructor * Creates new instance for PageIterator * @param {Client} client - The graph client instance @@ -77,7 +78,7 @@ export class PageIterator { * @param {PageIteratorCallback} callBack - The callback function * @returns An instance of a PageIterator */ - constructor(client: Client, pageCollection: PageCollection, callback: PageIteratorCallback) { + public constructor(client: Client, pageCollection: PageCollection, callback: PageIteratorCallback) { let self = this; self.client = client; self.collection = pageCollection.value;