From f48ed18b1c3a0de7090bb02b6dc7e485fafe7188 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Fri, 16 Aug 2019 11:08:21 +0530 Subject: [PATCH 01/55] updated broken link in README --- docs/CustomMiddlewareChain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/CustomMiddlewareChain.md b/docs/CustomMiddlewareChain.md index e42bdd59c..251942829 100644 --- a/docs/CustomMiddlewareChain.md +++ b/docs/CustomMiddlewareChain.md @@ -143,5 +143,4 @@ export class MyLoggingHandler implements Middleware { } } ``` - -Refer [MiddlewareOptions](../src/middleware/option/IMiddlewareOptions.ts) interface to know its structure. +Refer [MiddlewareOptions](../src/middleware/options/IMiddlewareOptions.ts) interface to know its structure. From 32daeb8bc48a4bf4a7e3ddadf5acf78923806883 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Mon, 16 Sep 2019 11:07:52 +0530 Subject: [PATCH 02/55] Post request with empty body, now working fine. --- src/GraphRequestUtil.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphRequestUtil.ts b/src/GraphRequestUtil.ts index c58062ad7..ab0ffae8f 100644 --- a/src/GraphRequestUtil.ts +++ b/src/GraphRequestUtil.ts @@ -41,7 +41,7 @@ export const urlJoin = (urlSegments: string[]): string => { */ export const serializeContent = (content: any): any => { - const className: string = content.constructor.name; + const className: string = content === undefined ? null : content.constructor.name; if (className === "Buffer" || className === "Blob" || className === "File" || className === "FormData" || typeof content === "string") { return content; } From 6850658762018c46c3a4d01368d5d8300139ca09 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Mon, 16 Sep 2019 16:22:13 +0530 Subject: [PATCH 03/55] updated the tests for this case --- spec/core/GraphRequestUtil.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/core/GraphRequestUtil.ts b/spec/core/GraphRequestUtil.ts index 6ce3dff82..021ef1905 100644 --- a/spec/core/GraphRequestUtil.ts +++ b/spec/core/GraphRequestUtil.ts @@ -61,10 +61,15 @@ describe("GraphRequestUtil.ts", () => { node2.link = node1; try { serializeContent(node1); - throw new Error("Something wrong with the serialize content, it should stringify cyclic referenced objects"); + throw new Error("Something wrong with the serialize content, it should not stringify cyclic referenced objects"); } catch (error) { assert.equal(error.message, "Unable to stringify the content"); } }); + + it("Should return undefined for the case of undefined content value", () => { + const val = undefined; + assert.equal(serializeContent(val), undefined); + }); }); }); From 2ebf66635d8b4c7d63d4a5b17c42d68149ab9137 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Tue, 17 Sep 2019 10:45:11 +0530 Subject: [PATCH 04/55] made change to use undefined instead of null as classname --- src/GraphRequestUtil.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphRequestUtil.ts b/src/GraphRequestUtil.ts index ab0ffae8f..c2afee5a5 100644 --- a/src/GraphRequestUtil.ts +++ b/src/GraphRequestUtil.ts @@ -41,7 +41,7 @@ export const urlJoin = (urlSegments: string[]): string => { */ export const serializeContent = (content: any): any => { - const className: string = content === undefined ? null : content.constructor.name; + const className: string = content === undefined ? undefined : content.constructor.name; if (className === "Buffer" || className === "Blob" || className === "File" || className === "FormData" || typeof content === "string") { return content; } From 834fcf3e6bba7433b80370c6ce1a2d17483ae96a Mon Sep 17 00:00:00 2001 From: krototype Date: Tue, 15 Oct 2019 15:55:38 +0530 Subject: [PATCH 05/55] added functionality to simplify building middleware chain --- spec/core/HTTPClient.ts | 39 +++++++++++++++++++++++++ src/Client.ts | 19 ++++++++++++ src/HTTPClient.ts | 31 ++++++++++++++++++++ src/middleware/AuthenticationHandler.ts | 9 ++++++ src/middleware/IMiddleware.ts | 1 + src/middleware/RedirectHandler.ts | 9 ++++++ src/middleware/RetryHandler.ts | 9 ++++++ src/middleware/TelemetryHandler.ts | 9 ++++++ 8 files changed, 126 insertions(+) diff --git a/spec/core/HTTPClient.ts b/spec/core/HTTPClient.ts index 8c00d9aee..f4a70c5ef 100644 --- a/spec/core/HTTPClient.ts +++ b/spec/core/HTTPClient.ts @@ -7,9 +7,13 @@ import { assert } from "chai"; +import { Client } from "../../src/Client"; import { HTTPClient } from "../../src/HTTPClient"; import { Context } from "../../src/IContext"; import { FetchOptions } from "../../src/IFetchOptions"; +import { RedirectHandlerOptions } from "../../src/middleware/options/RedirectHandlerOptions"; +import { RedirectHandler } from "../../src/middleware/RedirectHandler"; +import { TelemetryHandler } from "../../src/middleware/TelemetryHandler"; import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; describe("HTTPClient.ts", () => { @@ -68,4 +72,39 @@ describe("HTTPClient.ts", () => { } }); }); + + describe("getMiddlewareArray", () => { + it("Should work fine for a single middleware in the chain, which does have a getNext method", () => { + const telemetryHandler = new TelemetryHandler(); + const tempHttpClient: HTTPClient = new HTTPClient(telemetryHandler); + assert.equal(tempHttpClient.getMiddlewareArray().length, 1); + }); + + it("Should work fine for a single middleware in the chain, which doesn't have a getNext method", () => { + const tempHttpClient: HTTPClient = new HTTPClient(httpMessageHandler); + assert.equal(tempHttpClient.getMiddlewareArray().length, 1); + }); + + it("Should work fine for a chain containing many middlewares", () => { + const telemetryHandler = new TelemetryHandler(); + const redirectHandler = new RedirectHandler(new RedirectHandlerOptions()); + redirectHandler.setNext(telemetryHandler); + telemetryHandler.setNext(httpMessageHandler); + const tempHttpClient: HTTPClient = new HTTPClient(redirectHandler); + assert.equal(tempHttpClient.getMiddlewareArray().length, 3); + }); + }); + + describe("setMiddlewareArray", () => { + it("Should make a chain out of the provided array of middlewares", () => { + const telemetryHandler = new TelemetryHandler(); + const redirectHandler = new RedirectHandler(new RedirectHandlerOptions()); + redirectHandler.setNext(httpMessageHandler); + const tempHttpClient: HTTPClient = new HTTPClient(redirectHandler); + const middlewareArray = tempHttpClient.getMiddlewareArray(); + middlewareArray.splice(1, 0, telemetryHandler); + tempHttpClient.setMiddlewareArray(middlewareArray); + assert.equal(tempHttpClient.getMiddlewareArray().length, 3); + }); + }); }); diff --git a/src/Client.ts b/src/Client.ts index 0cd9b04c8..610144447 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -16,6 +16,7 @@ import { HTTPClient } from "./HTTPClient"; import { HTTPClientFactory } from "./HTTPClientFactory"; import { ClientOptions } from "./IClientOptions"; import { Options } from "./IOptions"; +import { Middleware } from "./middleware/IMiddleware"; import { validatePolyFilling } from "./ValidatePolyFilling"; export class Client { @@ -103,6 +104,24 @@ export class Client { this.httpClient = httpClient; } + /** + * @public + * function to get the array of middlewares in use right now + * @returns An array of middlewares + */ + public getMiddlewareChain() { + return this.httpClient.getMiddlewareArray(); + } + + /** + * @public + * function to set the middleware chain + * @param {Middleware[]} middlewareArray - An array of middlewares + */ + public setMiddlewareChain(middlewareArray: Middleware[]) { + return this.httpClient.setMiddlewareArray(middlewareArray); + } + /** * @public * Entry point to make requests diff --git a/src/HTTPClient.ts b/src/HTTPClient.ts index 2276fb227..ba4c3cf0a 100644 --- a/src/HTTPClient.ts +++ b/src/HTTPClient.ts @@ -33,6 +33,37 @@ export class HTTPClient { this.middleware = middleware; } + /** + * @public + * To get an array of Middleware, used in middleware chain + * @returns An array of middlewares + */ + public getMiddlewareArray(): Middleware[] { + const middlewareArray: Middleware[] = []; + let currentMiddleware = this.middleware; + while (currentMiddleware) { + middlewareArray.push(currentMiddleware); + if (typeof currentMiddleware.getNext !== "undefined") { + currentMiddleware = currentMiddleware.getNext(); + } else { + break; + } + } + return middlewareArray; + } + + /** + * @public + * To set the middleware chain + * @param {Middleware[]} middlewareArray - The array containing the middlewares + */ + public setMiddlewareArray(middlewareArray: Middleware[]) { + for (let num = 0; num < middlewareArray.length - 1; num += 1) { + middlewareArray[num].setNext(middlewareArray[num + 1]); + } + this.middleware = middlewareArray[0]; + } + /** * @public * @async diff --git a/src/middleware/AuthenticationHandler.ts b/src/middleware/AuthenticationHandler.ts index 3275c6092..358d900f9 100644 --- a/src/middleware/AuthenticationHandler.ts +++ b/src/middleware/AuthenticationHandler.ts @@ -94,4 +94,13 @@ export class AuthenticationHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } + + /** + * @public + * To get the next middleware in the chain + * @returns next Middleware instance + */ + public getNext(): Middleware { + return this.nextMiddleware; + } } diff --git a/src/middleware/IMiddleware.ts b/src/middleware/IMiddleware.ts index 70dacc703..51e0821d0 100644 --- a/src/middleware/IMiddleware.ts +++ b/src/middleware/IMiddleware.ts @@ -15,4 +15,5 @@ import { Context } from "../IContext"; export interface Middleware { execute: (context: Context) => Promise; setNext?: (middleware: Middleware) => void; + getNext?: () => Middleware; } diff --git a/src/middleware/RedirectHandler.ts b/src/middleware/RedirectHandler.ts index 4bdcc9e39..5878062e5 100644 --- a/src/middleware/RedirectHandler.ts +++ b/src/middleware/RedirectHandler.ts @@ -243,4 +243,13 @@ export class RedirectHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } + + /** + * @public + * To get the next middleware in the chain + * @returns next Middleware instance + */ + public getNext(): Middleware { + return this.nextMiddleware; + } } diff --git a/src/middleware/RetryHandler.ts b/src/middleware/RetryHandler.ts index 56b5c22c3..efd159a26 100644 --- a/src/middleware/RetryHandler.ts +++ b/src/middleware/RetryHandler.ts @@ -216,4 +216,13 @@ export class RetryHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } + + /** + * @public + * To get the next middleware in the chain + * @returns next Middleware instance + */ + public getNext(): Middleware { + return this.nextMiddleware; + } } diff --git a/src/middleware/TelemetryHandler.ts b/src/middleware/TelemetryHandler.ts index e72d2c187..1d1b09dec 100644 --- a/src/middleware/TelemetryHandler.ts +++ b/src/middleware/TelemetryHandler.ts @@ -96,4 +96,13 @@ export class TelemetryHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } + + /** + * @public + * To get the next middleware in the chain + * @returns next Middleware instance + */ + public getNext(): Middleware { + return this.nextMiddleware; + } } From 072bed148e2ed7ded31806ae0414bcd8fe9a42ec Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Tue, 22 Oct 2019 11:19:46 +0530 Subject: [PATCH 06/55] Updated GraphRequest.ts for .count() scenario Updated GraphRequest.ts to handle .count() when no parameter is specified --- src/GraphRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 79762b462..366ed1782 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -489,7 +489,7 @@ export class GraphRequest { * @param {boolean} isCount - The count boolean * @returns The same GraphRequest instance that is being called with */ - public count(isCount: boolean): GraphRequest { + public count(isCount: boolean = false): GraphRequest { this.urlComponents.oDataQueryParams.$count = isCount.toString(); return this; } From c3e0ef343c04f9ab19949f68a6a3569f43a65bbc Mon Sep 17 00:00:00 2001 From: muthurathinam Date: Fri, 25 Oct 2019 07:11:15 +0000 Subject: [PATCH 07/55] Bumped version to '2.1.0-Preview.1' --- package-lock.json | 2 +- package.json | 2 +- src/Version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ba1e60c7b..08478c7a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.0.0", + "version": "2.1.0-Preview.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5c170f7dc..30bc78dc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.0.0", + "version": "2.1.0-Preview.1", "description": "Microsoft Graph Client Library", "main": "lib/src/index.js", "module": "lib/es/index.js", diff --git a/src/Version.ts b/src/Version.ts index b67e5277e..bb4ac523c 100644 --- a/src/Version.ts +++ b/src/Version.ts @@ -12,4 +12,4 @@ * @module Version */ -export const PACKAGE_VERSION = "2.0.0"; +export const PACKAGE_VERSION = "2.1.0-Preview.1"; From 03749592e986347017e1cc128f4fabc3e80f56d6 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Tue, 12 Nov 2019 15:48:44 +0530 Subject: [PATCH 08/55] Adding the Modifying middleware chain samples --- docs/CustomMiddlewareChain.md | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/CustomMiddlewareChain.md b/docs/CustomMiddlewareChain.md index 251942829..775c54104 100644 --- a/docs/CustomMiddlewareChain.md +++ b/docs/CustomMiddlewareChain.md @@ -144,3 +144,40 @@ export class MyLoggingHandler implements Middleware { } ``` Refer [MiddlewareOptions](../src/middleware/options/IMiddlewareOptions.ts) interface to know its structure. + +### Modifying the Current Middleware Chain + +```js +// initialising client +const client = MicrosoftGraph.Client.init({ + defaultVersion: "v1.0", + debugLogging: true, + authProvider: (done) => { + done(null, secrets.accessToken); + }, +}); + +// getting the current middleware chain (in this case, it's the default one) +let arr = client.getMiddlewareChain(); + +// Initialising the Middleware chain that we created +const dummyRandomHandler = new dummyRandomHandler(); + +// adding the dummy handler in the array of middlewares at 3rd position +arr.splice(2, 0, dummyRandomHandler); + +// setting the new middleware chain +client.setMiddlewareChain(arr); + +// calling the api +client + .api("/me") + .select("displayName") + .get() + .then((res) => { + console.log(res); + }) + .catch((err) => { + console.log(err); + }); +``` From f825b1b744d5819192edcd9a06ead93c26a7aec2 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 18 Nov 2019 16:02:03 -0500 Subject: [PATCH 09/55] - fixes broken link to client instance --- docs/GettingRawResponse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingRawResponse.md b/docs/GettingRawResponse.md index 142cd063e..cf3169fec 100644 --- a/docs/GettingRawResponse.md +++ b/docs/GettingRawResponse.md @@ -4,7 +4,7 @@ Steps for getting the raw response [i.e [Response Object](https://developer.mozi ## Initialize the Client -Refer [this documentation](../CreatingClientInstance.md) for initializing the client. +Refer [this documentation](./CreatingClientInstance.md) for initializing the client. ## Getting Raw Response by setting ResponseType From c4e43a8af2cbe627a43f065107c88d65413fb1ad Mon Sep 17 00:00:00 2001 From: warreee Date: Tue, 19 Nov 2019 16:33:45 +0100 Subject: [PATCH 10/55] Updated the example to the correct return type --- docs/CustomAuthenticationProvider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CustomAuthenticationProvider.md b/docs/CustomAuthenticationProvider.md index 0b590b130..625adf944 100644 --- a/docs/CustomAuthenticationProvider.md +++ b/docs/CustomAuthenticationProvider.md @@ -18,7 +18,7 @@ class MyAuthenticationProvider implements AuthenticationProvider { * This should return a Promise that resolves to an accessToken (in case of success) or rejects with error (in case of failure) * Basically this method will contain the implementation for getting and refreshing accessTokens */ - public async getAccessToken(): Promise {} + public async getAccessToken(): Promise {} } ``` From 73e0b1ea5525de5591763cc96d8b63bb037980c4 Mon Sep 17 00:00:00 2001 From: Gideon Goldberg Date: Fri, 29 Nov 2019 10:57:39 +0000 Subject: [PATCH 11/55] fix casing on filename --- docs/tasks/LargeFileUploadTask.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tasks/LargeFileUploadTask.md b/docs/tasks/LargeFileUploadTask.md index d1b7af09b..981eff3f7 100644 --- a/docs/tasks/LargeFileUploadTask.md +++ b/docs/tasks/LargeFileUploadTask.md @@ -104,7 +104,7 @@ _You can pass in the customized options using LargeFileUploadTask_ ```typescript async function largeFileUpload(client, file) { - const filename = file.name; + const fileName = file.name; const driveId = ""; const path = ""; try { From db7403de52aada8f5f78f0e5c940da2359f5332c Mon Sep 17 00:00:00 2001 From: Nikola Metulev Date: Wed, 11 Dec 2019 15:06:46 -0800 Subject: [PATCH 12/55] Update package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 30bc78dc7..033467a57 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "@microsoft/microsoft-graph-client", "version": "2.1.0-Preview.1", "description": "Microsoft Graph Client Library", + "license": "MIT", "main": "lib/src/index.js", "module": "lib/es/index.js", "typings": "lib/src/index", From 9da92e1502336aaa3c86952bb74baa4f4600a600 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Thu, 12 Dec 2019 14:57:47 +0530 Subject: [PATCH 13/55] Designed ChaosHandler --- docs/ChaosHandlerSamples.md | 102 ++++++ docs/content/Batching.md | 2 +- spec/middleware/ChaosHandler.ts | 254 +++++++++++++++ src/middleware/ChaosHandler.ts | 291 ++++++++++++++++++ src/middleware/options/ChaosHandlerData.ts | 88 ++++++ src/middleware/options/ChaosHandlerOptions.ts | 69 +++++ src/middleware/options/ChaosStrategy.ts | 19 ++ 7 files changed, 824 insertions(+), 1 deletion(-) create mode 100644 docs/ChaosHandlerSamples.md create mode 100644 spec/middleware/ChaosHandler.ts create mode 100644 src/middleware/ChaosHandler.ts create mode 100644 src/middleware/options/ChaosHandlerData.ts create mode 100644 src/middleware/options/ChaosHandlerOptions.ts create mode 100644 src/middleware/options/ChaosStrategy.ts diff --git a/docs/ChaosHandlerSamples.md b/docs/ChaosHandlerSamples.md new file mode 100644 index 000000000..cb3f8cb2f --- /dev/null +++ b/docs/ChaosHandlerSamples.md @@ -0,0 +1,102 @@ +# Testing Handler + +### How to include + +> Uses [Custom Middleware Chain](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CustomMiddlewareChain.md), it's not included in default middleware chain + +### Modes in Testing Handler + +- Manual mode - Setting the Response code manually. - Global/Client Level - Provide a map declared manually containing response code for the requests. - Request Level - Providing response code per request. This would be overriding the Global level response code (if any). +- Random mode - We get a random Response code from a set of response code defined for each method. + +### Samples + +```js +require("isomorphic-fetch"); +const MicrosoftGraph = require("../../lib/src/index.js"); +const secrets = require("./secrets"); +const fs = require("fs"); +// Initialising the client +const client = MicrosoftGraph.Client.init({ + defaultVersion: "v1.0", + debugLogging: true, + authProvider: (done) => { + done(null, secrets.accessToken); + }, +}); + +// Declaring the Map, containing response codes for the urls +const manualMap = new Map([["/me/messages/.*", new Map([["GET", 429], ["PATCH", 429]])], ["/me", new Map([["POST", 502]])]]); + +// Declaring the chaosHandler and passing the map (if using map, we have to put default strategy as MANUAL) +const chaosHandler = new MicrosoftGraph.ChaosHandler(new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.MANUAL), manualMap); + +// Modifying the default middleware chain to add chaos handler, just before httpMessageHandler, otherwise there can be a problem +let arr = client.getMiddlewareChain(); +arr.splice(arr.length - 1, 0, chaosHandler); +client.setMiddlewareChain(arr); + +// This request would use the Map (Manual mode) +const mail = { + subject: "Chaos Handler Samples", + toRecipients: [ + { + emailAddress: { + address: "admin@M365x003297.OnMicrosoft.com", + }, + }, + ], + body: { + content: "

Testing Handler Samples Sample


https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +client + .api("/users/me/sendMail") + .post({ + message: mail, + }) + .then((res) => { + console.log(res, "This is for sendMail"); + }) + .catch((err) => { + console.log(err, "This is for sendMail in error case"); + }); + +// OverRiding to Random mode, providing the chaos percentage as 60(percentage times the error would be generated from handler) +client + .api("/me") + .middlewareOptions([new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.RANDOM, undefined, "I generated the error", 60)]) + .get() + .then((res) => { + console.log(res); + }) + .catch((err) => { + console.log(err); + }); + +// This request is passed to the graph and gets a response from the graph, as no entry for /me GET request in the Map +client + .api("/me") + .get() + .then((res) => { + console.log("Found", res, "users"); + }) + .catch((err) => { + console.log(err, "!!!!!!!!!"); + }); + +// Using Manual Map with regex matching +client + .api("/me/messages/hjdlfslod-fdssdkjfs-6zdkmghs-sadhsu2") + .header("content-type", "application/json") + .update({ + birthday: "1908-12-22T00:00:00Z", + }) + .then((res) => { + console.log("This is regex matching... Updated Bday"); + }) + .catch((err) => { + console.log(err, "matched"); + }); +``` diff --git a/docs/content/Batching.md b/docs/content/Batching.md index 9c40b280d..68405ac54 100644 --- a/docs/content/Batching.md +++ b/docs/content/Batching.md @@ -56,7 +56,7 @@ const serialBatching = async function(elem) { let batchResponseContent = new MicrosoftGraph.BatchResponseContent(response); //Getting response by id - console.log(batchResponse.getResponseById(downloadId)); + console.log(batchResponseContent.getResponseById(downloadId)); //Getting all the responses console.log(batchResponseContent.getResponses()); diff --git a/spec/middleware/ChaosHandler.ts b/spec/middleware/ChaosHandler.ts new file mode 100644 index 000000000..8e6f605aa --- /dev/null +++ b/spec/middleware/ChaosHandler.ts @@ -0,0 +1,254 @@ +import { assert } from "chai"; + +import { Context } from "../../src/IContext"; +import { ChaosHandler } from "../../src/middleware/ChaosHandler"; +import { MiddlewareControl } from "../../src/middleware/MiddlewareControl"; +import { ChaosHandlerOptions } from "../../src/middleware/options/ChaosHandlerOptions"; +import { ChaosStrategy } from "../../src/middleware/options/ChaosStrategy"; +import { RequestMethod } from "../../src/RequestMethod"; +import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; + +const chaosHandlerOptions = new ChaosHandlerOptions(); +const chaosHandler = new ChaosHandler(); + +describe("ChaosHandler.ts", () => { + /* tslint:disable: no-string-literal */ + describe("constructor", () => { + it("Should create an instance with given options", () => { + const handler = new ChaosHandler(chaosHandlerOptions); + assert.isDefined(handler["options"]); + }); + + it("Should create an instance with default set of options", () => { + const handler = new ChaosHandler(); + assert.isDefined(handler["options"]); + }); + }); + + describe("createResponseHeaders", () => { + it("Should have request-id for every random statusCode", () => { + const responseHeader = chaosHandler["createResponseHeaders"](204, "xxxxxxxxxxxxxxxx", new Date().toString()); + assert.isDefined(responseHeader.get("request-id")); + }); + + it("Should have retry-after for 429 case", () => { + const responseHeader = chaosHandler["createResponseHeaders"](429, "xxxxxxxxxxxxxxxx", new Date().toString()); + assert.isDefined(responseHeader.get("retry-after")); + }); + }); + + describe("createResponseBody", () => { + it("Should return error in response body for error scenarios", () => { + const responseBody = chaosHandler["createResponseBody"](404, "Not Found", "xxxxxxxxxxxxxx", new Date().toString()); + assert.isDefined(responseBody["error"]); + }); + + it("Should return empty response body for success scenarios", () => { + const responseBody = chaosHandler["createResponseBody"](200, "Not Found", "xxxxxxxxxxxxxx", new Date().toString()); + assert.equal(Object.keys(responseBody).length, 0); + }); + }); + + describe("createResponse", () => { + const cxt: Context = { + request: "https://graph.microsoft.com/v1.0/me", + options: { + method: "GET", + }, + }; + + it("Should return a valid response object for MANUAL case", () => { + chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404), cxt); + assert.isDefined(cxt.response); + }); + + it("Should return a valid response object for RANDOM case", () => { + chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.RANDOM), cxt); + assert.isDefined(cxt.response); + }); + }); + + describe("sendRequest", async () => { + const cxt: Context = { + request: "https://graph.microsoft.com/v1.0/me", + options: { + method: "GET", + }, + }; + + const manualMap: Map> = new Map([["/me", new Map([["GET", 500]])]]); + const tempManualOptions: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL); + const tempChaosHandler = new ChaosHandler(tempManualOptions, manualMap); + + const dummyHTTPHandler = new DummyHTTPMessageHandler(); + const handler = new ChaosHandler(); + handler.setNext(dummyHTTPHandler); + + it("Should return a response after creating it", async () => { + tempChaosHandler["sendRequest"](tempManualOptions, cxt); + assert.isDefined(cxt.response); + }); + + it("Should send the request to the graph", async () => { + handler["sendRequest"](new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100), cxt); + assert.isDefined(cxt.response); + }); + }); + + describe("getRandomStatusCode", () => { + it("Should return a status code for GET method", () => { + assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.GET)); + }); + + it("Should return a status code for POST method", () => { + assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.POST)); + }); + + it("Should return a status code for PUT method", () => { + assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.PUT)); + }); + + it("Should return a status code for PATCH method", () => { + assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.PATCH)); + }); + + it("Should return a status code for DELETE method", () => { + assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.DELETE)); + }); + }); + + describe("getRelativeURL", () => { + it("Should return a relative URL for the complete URL", () => { + assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me"), "/me"); + }); + + it("Should return a relative URL for the complete URL with filter", () => { + assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me/messages?filter=emailAddress eq 'jon@contoso.com'"), "/me/messages"); + }); + + it("Should return a relative URL for the complete URL with ids", () => { + assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me/messages/q1abcxx-xxxxxx-xxxxabc"), "/me/messages/q1abcxx-xxxxxx-xxxxabc"); + }); + + it("Should return a relative URL for the complete URL in case of beta", () => { + assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/beta/me/messages"), "/me/messages"); + }); + }); + + describe("setStatusCode", () => { + const manualMap: Map> = new Map([["/me/messages/.*", new Map([["GET", 500], ["PATCH", 201]])], ["/me", new Map([["GET", 500], ["PATCH", 201]])]]); + const tempManualOptions: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL); + const tempManualOptionsRegex: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL); + const tempChaosHandlerManual = new ChaosHandler(tempManualOptions, manualMap); + const tempChaosHandlerManualRegex = new ChaosHandler(tempManualOptionsRegex, manualMap); + + it("Should set a statusCode for MANUAL mode", () => { + const tempOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404); + chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.GET); + assert.isDefined(tempOptions.statusCode); + }); + + it("Should set a statusCode for RANDOM mode", () => { + const tempOptions = new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100); + chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.POST); + assert.isDefined(tempOptions.statusCode); + }); + + it("Should set a statusCode for MANUAL mode with manualMap", () => { + tempChaosHandlerManual["setStatusCode"](tempManualOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.PATCH); + assert.equal(tempManualOptions.statusCode, 201); + }); + + it("Should set a statusCode for MANUAL mode with manualMap matching regex", () => { + tempChaosHandlerManualRegex["setStatusCode"](tempManualOptionsRegex, "https://graph.microsoft.com/v1.0/me/messages/abc123-xxxxx-xxxxx", RequestMethod.GET); + assert.equal(tempManualOptionsRegex.statusCode, 500); + }); + }); + + describe("getOptions", () => { + it("Should return the options in the context object", () => { + const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 405); + const cxt: Context = { + request: "url", + middlewareControl: new MiddlewareControl([options]), + }; + const o = chaosHandler["getOptions"](cxt); + assert.equal(o.chaosStrategy, ChaosStrategy.MANUAL); + assert.equal(o.statusCode, 405); + }); + + it("Should return the default set of options with RANDOM in the middleware", () => { + const cxt: Context = { + request: "url", + }; + const o = chaosHandler["getOptions"](cxt); + assert.equal(o.chaosStrategy, ChaosStrategy.RANDOM); + assert.equal(o.statusCode, undefined); + }); + + it("Should return the default set of options with DEFAULT in the middleware", () => { + const tempChaosHandler = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.MANUAL)); + const cxt: Context = { + request: "url", + }; + const o = tempChaosHandler["getOptions"](cxt); + assert.equal(o.chaosStrategy, ChaosStrategy.MANUAL); + assert.equal(o.statusCode, undefined); + }); + }); + + describe("execute", async () => { + const manualMap: Map> = new Map([["/me", new Map([["GET", 500], ["PATCH", 201]])]]); + const dummyHTTPHandler = new DummyHTTPMessageHandler(); + const tempChaosHandlerDefault = new ChaosHandler(new ChaosHandlerOptions()); + const tempChaosHandlerRandom = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.RANDOM)); + const tempChaosHandlerManual = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.MANUAL), manualMap); + tempChaosHandlerDefault.setNext(dummyHTTPHandler); + tempChaosHandlerRandom.setNext(dummyHTTPHandler); + tempChaosHandlerManual.setNext(dummyHTTPHandler); + + it("Should return response for Default Case", async () => { + const options = new ChaosHandlerOptions(ChaosStrategy.RANDOM); + const cxt: Context = { + request: "https://graph.microsoft.com/v1.0/me", + options: { + method: "GET", + }, + middlewareControl: new MiddlewareControl([options]), + }; + assert.isDefined(tempChaosHandlerDefault["execute"](cxt)); + }); + + it("Should return response for Random case", async () => { + const cxt: Context = { + request: "https://graph.microsoft.com/v1.0/me", + options: { + method: "GET", + }, + }; + assert.isDefined(tempChaosHandlerRandom["execute"](cxt)); + }); + + it("Should return response for Manual Global case", async () => { + const cxt: Context = { + request: "https://graph.microsoft.com/v1.0/me", + options: { + method: "GET", + }, + }; + assert.isDefined(tempChaosHandlerManual["execute"](cxt)); + }); + + it("Should return response for Manual Request Level case", async () => { + const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200); + const cxt: Context = { + request: "https://graph.microsoft.com/v1.0/me", + options: { + method: "GET", + }, + middlewareControl: new MiddlewareControl([options]), + }; + assert.isDefined(tempChaosHandlerManual["execute"](cxt)); + }); + }); +}); diff --git a/src/middleware/ChaosHandler.ts b/src/middleware/ChaosHandler.ts new file mode 100644 index 000000000..bc0ec53c1 --- /dev/null +++ b/src/middleware/ChaosHandler.ts @@ -0,0 +1,291 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module ChaosHandler + */ + +import { Context } from "../IContext"; +import { RequestMethod } from "../RequestMethod"; + +import { Middleware } from "./IMiddleware"; +import { MiddlewareControl } from "./MiddlewareControl"; +import { generateUUID } from "./MiddlewareUtil"; +import { httpStatusCode, methodStatusCode } from "./options/ChaosHandlerData"; +import { ChaosHandlerOptions } from "./options/ChaosHandlerOptions"; +import { ChaosStrategy } from "./options/ChaosStrategy"; + +/** + * Class representing ChaosHandler + * @class + * Class + * @implements Middleware + */ +export class ChaosHandler implements Middleware { + /** + * A member holding options to customize the handler behavior + * + * @private + */ + private options: ChaosHandlerOptions; + + /** + * container for the manual map that has been written by the client + * + * @private + */ + private manualMap: Map>; + + /** + * @private + * A member to hold next middleware in the middleware chain + */ + private nextMiddleware: Middleware; + + /** + * @public + * @constructor + * To create an instance of Testing Handler + * @param {ChaosHandlerOptions} [options = new ChaosHandlerOptions()] - The testing handler options instance + * @param manualMap - The Map passed by user containing url-statusCode info + * @returns An instance of Testing Handler + */ + public constructor(options: ChaosHandlerOptions = new ChaosHandlerOptions(), manualMap?: Map>) { + this.options = options; + this.manualMap = manualMap; + } + + /** + * Generates responseHeader + * @private + * @param {number} statusCode - the status code to be returned for the request + * @param {string} requestID - request id + * @param {string} requestDate - date of the request + * @returns response Header + */ + private createResponseHeaders(statusCode: number, requestID: string, requestDate: string) { + const responseHeader: Headers = new Headers(); + + responseHeader.append("Cache-Control", "no-store"); + responseHeader.append("request-id", requestID); + responseHeader.append("client-request-id", requestID); + responseHeader.append("x-ms-ags-diagnostic", ""); + responseHeader.append("Date", requestDate); + responseHeader.append("Strict-Transport-Security", ""); + + if (statusCode === 429) { + // throttling case has to have a timeout scenario + responseHeader.append("retry-after", "300"); + } + return responseHeader; + } + + /** + * Generates responseBody + * @private + * @param {number} statusCode - the status code to be returned for the request + * @param {string} statusMessage - the status message to be returned for the request + * @param {string} requestID - request id + * @param {string} requestDate - date of the request + * @returns response body + */ + private createResponseBody(statusCode: number, statusMessage: string, requestID: string, requestDate: string) { + let responseBody: any; + if (statusCode >= 400) { + const codeMessage: string = httpStatusCode[statusCode]; + const errMessage: string = statusMessage; + + responseBody = { + error: { + code: codeMessage, + message: errMessage, + innerError: { + "request-id": requestID, + date: requestDate, + }, + }, + }; + } else { + responseBody = {}; + } + return responseBody; + } + + /** + * creates a response + * @private + * @param {ChaosHandlerOptions} ChaosHandlerOptions - The ChaosHandlerOptions object + * @param {Context} context - Contains the context of the request + */ + private createResponse(chaosHandlerOptions: ChaosHandlerOptions, context: Context) { + try { + let responseBody: any; + let responseHeader: Headers; + let requestID: string; + let requestDate: Date; + const requestURL = context.request as string; + + requestID = generateUUID(); + requestDate = new Date(); + responseHeader = this.createResponseHeaders(chaosHandlerOptions.statusCode, requestID, requestDate.toString()); + responseBody = this.createResponseBody(chaosHandlerOptions.statusCode, chaosHandlerOptions.statusMessage, requestID, requestDate.toString()); + const init: any = { url: requestURL, status: chaosHandlerOptions.statusCode, statusText: chaosHandlerOptions.statusMessage, headers: responseHeader }; + context.response = new Response(responseBody, init); + } catch (error) { + throw error; + } + } + + /** + * Decides whether to send the request to the graph or not + * @private + * @param {ChaosHandlerOptions} chaosHandlerOptions - A ChaosHandlerOptions object + * @param {Context} context - Contains the context of the request + * @returns nothing + */ + private async sendRequest(chaosHandlerOptions: ChaosHandlerOptions, context: Context): Promise { + try { + this.setStatusCode(chaosHandlerOptions, context.request as string, context.options.method as RequestMethod); + if (!chaosHandlerOptions.statusCode) { + await this.nextMiddleware.execute(context); + } else { + this.createResponse(chaosHandlerOptions, context); + } + } catch (error) { + throw error; + } + } + + /** + * Fetches a random status code for the RANDOM mode from the predefined array + * @private + * @param {string} requestMethod - the API method for the request + * @returns a random status code from a given set of status codes + */ + private getRandomStatusCode(requestMethod: RequestMethod): number { + try { + const statusCodeArray: number[] = methodStatusCode[requestMethod] as number[]; + return statusCodeArray[Math.floor(Math.random() * statusCodeArray.length)]; + } catch (error) { + throw error; + } + } + + /** + * To fetch the relative URL out of the complete URL using a predefined regex pattern + * @private + * @param {string} urlMethod - the complete URL + * @returns the string as relative URL + */ + private getRelativeURL(urlMethod: string): string { + const pattern: RegExp = /https?:\/\/graph\.microsoft\.com\/[^/]+(.+?)(\?|$)/; + let relativeURL: string; + if (pattern.exec(urlMethod) !== null) { + relativeURL = pattern.exec(urlMethod)[1]; + } + return relativeURL; + } + + /** + * To fetch the status code from the map(if needed), then returns response by calling createResponse + * @private + * @param {ChaosHandlerOptions} ChaosHandlerOptions - The ChaosHandlerOptions object + * @param {string} requestURL - the URL for the request + * @param {string} requestMethod - the API method for the request + */ + private setStatusCode(chaosHandlerOptions: ChaosHandlerOptions, requestURL: string, requestMethod: RequestMethod) { + try { + if (chaosHandlerOptions.chaosStrategy === ChaosStrategy.MANUAL) { + if (chaosHandlerOptions.statusCode === undefined) { + // manual mode with no status code, can be a global level or request level without statusCode + const relativeURL: string = this.getRelativeURL(requestURL); + if (this.manualMap.get(relativeURL) !== undefined) { + // checking Manual Map for exact match + if (this.manualMap.get(relativeURL).get(requestMethod) !== undefined) { + chaosHandlerOptions.statusCode = this.manualMap.get(relativeURL).get(requestMethod); + } + // else statusCode would be undefined + } else { + // checking for regex match if exact match doesn't work + this.manualMap.forEach((value: Map, key: string) => { + const regexURL: RegExp = new RegExp(key + "$"); + if (regexURL.test(relativeURL)) { + if (this.manualMap.get(key).get(requestMethod) !== undefined) { + chaosHandlerOptions.statusCode = this.manualMap.get(key).get(requestMethod); + } + // else statusCode would be undefined + } + }); + } + + // Case of redirection or request url not in map ---> statusCode would be undefined + } + } else { + // Handling the case of Random here + if (Math.floor(Math.random() * 100) < chaosHandlerOptions.chaosPercentage) { + chaosHandlerOptions.statusCode = this.getRandomStatusCode(requestMethod); + } + // else statusCode would be undefined + } + } catch (error) { + throw error; + } + } + + /** + * To get the options for execution of the middleware + * @private + * @param {Context} context - The context object + * @returns options for middleware execution + */ + private getOptions(context: Context): ChaosHandlerOptions { + let options: ChaosHandlerOptions; + if (context.middlewareControl instanceof MiddlewareControl) { + options = context.middlewareControl.getMiddlewareOptions(ChaosHandlerOptions) as ChaosHandlerOptions; + } + if (typeof options === "undefined") { + options = Object.assign(new ChaosHandlerOptions(), this.options); + } + + return options; + } + + /** + * To execute the current middleware + * @public + * @async + * @param {Context} context - The context object of the request + * @returns A Promise that resolves to nothing + */ + public async execute(context: Context): Promise { + try { + const chaosHandlerOptions: ChaosHandlerOptions = this.getOptions(context); + return await this.sendRequest(chaosHandlerOptions, context); + } catch (error) { + throw error; + } + } + + /** + * @public + * To set the next middleware in the chain + * @param {Middleware} next - The middleware instance + * @returns Nothing + */ + public setNext(next: Middleware): void { + this.nextMiddleware = next; + } + + /** + * @public + * To get the next middleware in the chain + * @returns next Middleware instance + */ + public getNext(): Middleware { + return this.nextMiddleware; + } +} diff --git a/src/middleware/options/ChaosHandlerData.ts b/src/middleware/options/ChaosHandlerData.ts new file mode 100644 index 000000000..2b3a5924f --- /dev/null +++ b/src/middleware/options/ChaosHandlerData.ts @@ -0,0 +1,88 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module ChaosHandlerData + */ + +/** + * Contains RequestMethod to corresponding array of possible status codes, used for Random mode + */ +export const methodStatusCode: { [key: string]: number[] } = { + GET: [429, 500, 502, 503, 504], + POST: [429, 500, 502, 503, 504, 507], + PUT: [429, 500, 502, 503, 504, 507], + PATCH: [429, 500, 502, 503, 504], + DELETE: [429, 500, 502, 503, 504, 507], +}; + +/** + * Contains statusCode to statusMessage map + */ +export const httpStatusCode: { [key: number]: string } = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Early Hints", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Payload Too Large", + 414: "URI Too Long", + 415: "Unsupported Media Type", + 416: "Range Not Satisfiable", + 417: "Expectation Failed", + 421: "Misdirected Request", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 425: "Too Early", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 510: "Not Extended", + 511: "Network Authentication Required", +}; diff --git a/src/middleware/options/ChaosHandlerOptions.ts b/src/middleware/options/ChaosHandlerOptions.ts new file mode 100644 index 000000000..c74fe76cb --- /dev/null +++ b/src/middleware/options/ChaosHandlerOptions.ts @@ -0,0 +1,69 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module ChaosHandlerOptions + */ + +import { ChaosStrategy } from "./ChaosStrategy"; +import { MiddlewareOptions } from "./IMiddlewareOptions"; + +/** + * Class representing ChaosHandlerOptions + * @class + * Class + * @implements MiddlewareOptions + */ +export class ChaosHandlerOptions implements MiddlewareOptions { + /** + * Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL + * + * @public + */ + public chaosStrategy: ChaosStrategy; + + /** + * Status code to be returned in the response + * + * @public + */ + public statusCode: number; + + /** + * The Message to be returned in the response + * + * @public + */ + public statusMessage: string; + + /** + * The percentage of randomness/chaos in the handler + * + * Setting the default value as 10% + * @public + */ + public chaosPercentage: number; + + /** + * @public + * @constructor + * To create an instance of Testing Handler Options + * @param {ChaosStrategy} ChaosStrategy - Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL + * @param {number?} statusCode - The Message to be returned in the response + * @param {string} - The Message to be returned in the response + * @returns An instance of ChaosHandlerOptions + */ + public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusCode?: number, statusMessage: string = "Some error Happened", chaosPercentage?: number) { + this.chaosStrategy = chaosStrategy; + this.statusCode = statusCode; + this.statusMessage = statusMessage; + this.chaosPercentage = chaosPercentage !== undefined ? chaosPercentage : 10; + if (this.chaosPercentage > 100) { + throw new Error("Error Pecentage can not be more than 100"); + } + } +} diff --git a/src/middleware/options/ChaosStrategy.ts b/src/middleware/options/ChaosStrategy.ts new file mode 100644 index 000000000..5450f86df --- /dev/null +++ b/src/middleware/options/ChaosStrategy.ts @@ -0,0 +1,19 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module ChaosStrategy + */ + +/** + * Strategy used for Testing Handler + * @enum + */ +export enum ChaosStrategy { + MANUAL, + RANDOM, +} From aa17391479985c8cbe4ab4eb0093672a4db79aa2 Mon Sep 17 00:00:00 2001 From: Abhinav Srivastava Date: Thu, 12 Dec 2019 16:48:53 +0530 Subject: [PATCH 14/55] Removing the set and get middleware chain method --- docs/ChaosHandlerSamples.md | 18 ++++++------ docs/CustomMiddlewareChain.md | 38 +----------------------- spec/core/HTTPClient.ts | 39 ------------------------- src/Client.ts | 19 ------------ src/HTTPClient.ts | 31 -------------------- src/middleware/AuthenticationHandler.ts | 9 ------ src/middleware/ChaosHandler.ts | 9 ------ src/middleware/IMiddleware.ts | 1 - src/middleware/RedirectHandler.ts | 9 ------ src/middleware/RetryHandler.ts | 9 ------ src/middleware/TelemetryHandler.ts | 9 ------ 11 files changed, 10 insertions(+), 181 deletions(-) diff --git a/docs/ChaosHandlerSamples.md b/docs/ChaosHandlerSamples.md index cb3f8cb2f..f0e3887b3 100644 --- a/docs/ChaosHandlerSamples.md +++ b/docs/ChaosHandlerSamples.md @@ -4,11 +4,15 @@ > Uses [Custom Middleware Chain](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CustomMiddlewareChain.md), it's not included in default middleware chain -### Modes in Testing Handler +### Modes in Chaos Handler - Manual mode - Setting the Response code manually. - Global/Client Level - Provide a map declared manually containing response code for the requests. - Request Level - Providing response code per request. This would be overriding the Global level response code (if any). - Random mode - We get a random Response code from a set of response code defined for each method. +**A request Passes through to the Graph, if there is no entry for the request** + +**Note - Always add ChaosHandler before HttpMessageHandler in the midllewareChain** + ### Samples ```js @@ -25,16 +29,12 @@ const client = MicrosoftGraph.Client.init({ }, }); -// Declaring the Map, containing response codes for the urls -const manualMap = new Map([["/me/messages/.*", new Map([["GET", 429], ["PATCH", 429]])], ["/me", new Map([["POST", 502]])]]); +/* +Create a custom MiddlewareChain passing information in this way -// Declaring the chaosHandler and passing the map (if using map, we have to put default strategy as MANUAL) +const manualMap = new Map([["/me/messages/.*", new Map([["GET", 429], ["PATCH", 429]])], ["/me", new Map([["POST", 502]])]]); const chaosHandler = new MicrosoftGraph.ChaosHandler(new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.MANUAL), manualMap); - -// Modifying the default middleware chain to add chaos handler, just before httpMessageHandler, otherwise there can be a problem -let arr = client.getMiddlewareChain(); -arr.splice(arr.length - 1, 0, chaosHandler); -client.setMiddlewareChain(arr); +*/ // This request would use the Map (Manual mode) const mail = { diff --git a/docs/CustomMiddlewareChain.md b/docs/CustomMiddlewareChain.md index 775c54104..d86163088 100644 --- a/docs/CustomMiddlewareChain.md +++ b/docs/CustomMiddlewareChain.md @@ -143,41 +143,5 @@ export class MyLoggingHandler implements Middleware { } } ``` -Refer [MiddlewareOptions](../src/middleware/options/IMiddlewareOptions.ts) interface to know its structure. - -### Modifying the Current Middleware Chain -```js -// initialising client -const client = MicrosoftGraph.Client.init({ - defaultVersion: "v1.0", - debugLogging: true, - authProvider: (done) => { - done(null, secrets.accessToken); - }, -}); - -// getting the current middleware chain (in this case, it's the default one) -let arr = client.getMiddlewareChain(); - -// Initialising the Middleware chain that we created -const dummyRandomHandler = new dummyRandomHandler(); - -// adding the dummy handler in the array of middlewares at 3rd position -arr.splice(2, 0, dummyRandomHandler); - -// setting the new middleware chain -client.setMiddlewareChain(arr); - -// calling the api -client - .api("/me") - .select("displayName") - .get() - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); -``` +Refer [MiddlewareOptions](../src/middleware/options/IMiddlewareOptions.ts) interface to know its structure. diff --git a/spec/core/HTTPClient.ts b/spec/core/HTTPClient.ts index f4a70c5ef..8c00d9aee 100644 --- a/spec/core/HTTPClient.ts +++ b/spec/core/HTTPClient.ts @@ -7,13 +7,9 @@ import { assert } from "chai"; -import { Client } from "../../src/Client"; import { HTTPClient } from "../../src/HTTPClient"; import { Context } from "../../src/IContext"; import { FetchOptions } from "../../src/IFetchOptions"; -import { RedirectHandlerOptions } from "../../src/middleware/options/RedirectHandlerOptions"; -import { RedirectHandler } from "../../src/middleware/RedirectHandler"; -import { TelemetryHandler } from "../../src/middleware/TelemetryHandler"; import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; describe("HTTPClient.ts", () => { @@ -72,39 +68,4 @@ describe("HTTPClient.ts", () => { } }); }); - - describe("getMiddlewareArray", () => { - it("Should work fine for a single middleware in the chain, which does have a getNext method", () => { - const telemetryHandler = new TelemetryHandler(); - const tempHttpClient: HTTPClient = new HTTPClient(telemetryHandler); - assert.equal(tempHttpClient.getMiddlewareArray().length, 1); - }); - - it("Should work fine for a single middleware in the chain, which doesn't have a getNext method", () => { - const tempHttpClient: HTTPClient = new HTTPClient(httpMessageHandler); - assert.equal(tempHttpClient.getMiddlewareArray().length, 1); - }); - - it("Should work fine for a chain containing many middlewares", () => { - const telemetryHandler = new TelemetryHandler(); - const redirectHandler = new RedirectHandler(new RedirectHandlerOptions()); - redirectHandler.setNext(telemetryHandler); - telemetryHandler.setNext(httpMessageHandler); - const tempHttpClient: HTTPClient = new HTTPClient(redirectHandler); - assert.equal(tempHttpClient.getMiddlewareArray().length, 3); - }); - }); - - describe("setMiddlewareArray", () => { - it("Should make a chain out of the provided array of middlewares", () => { - const telemetryHandler = new TelemetryHandler(); - const redirectHandler = new RedirectHandler(new RedirectHandlerOptions()); - redirectHandler.setNext(httpMessageHandler); - const tempHttpClient: HTTPClient = new HTTPClient(redirectHandler); - const middlewareArray = tempHttpClient.getMiddlewareArray(); - middlewareArray.splice(1, 0, telemetryHandler); - tempHttpClient.setMiddlewareArray(middlewareArray); - assert.equal(tempHttpClient.getMiddlewareArray().length, 3); - }); - }); }); diff --git a/src/Client.ts b/src/Client.ts index 610144447..0cd9b04c8 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -16,7 +16,6 @@ import { HTTPClient } from "./HTTPClient"; import { HTTPClientFactory } from "./HTTPClientFactory"; import { ClientOptions } from "./IClientOptions"; import { Options } from "./IOptions"; -import { Middleware } from "./middleware/IMiddleware"; import { validatePolyFilling } from "./ValidatePolyFilling"; export class Client { @@ -104,24 +103,6 @@ export class Client { this.httpClient = httpClient; } - /** - * @public - * function to get the array of middlewares in use right now - * @returns An array of middlewares - */ - public getMiddlewareChain() { - return this.httpClient.getMiddlewareArray(); - } - - /** - * @public - * function to set the middleware chain - * @param {Middleware[]} middlewareArray - An array of middlewares - */ - public setMiddlewareChain(middlewareArray: Middleware[]) { - return this.httpClient.setMiddlewareArray(middlewareArray); - } - /** * @public * Entry point to make requests diff --git a/src/HTTPClient.ts b/src/HTTPClient.ts index ba4c3cf0a..2276fb227 100644 --- a/src/HTTPClient.ts +++ b/src/HTTPClient.ts @@ -33,37 +33,6 @@ export class HTTPClient { this.middleware = middleware; } - /** - * @public - * To get an array of Middleware, used in middleware chain - * @returns An array of middlewares - */ - public getMiddlewareArray(): Middleware[] { - const middlewareArray: Middleware[] = []; - let currentMiddleware = this.middleware; - while (currentMiddleware) { - middlewareArray.push(currentMiddleware); - if (typeof currentMiddleware.getNext !== "undefined") { - currentMiddleware = currentMiddleware.getNext(); - } else { - break; - } - } - return middlewareArray; - } - - /** - * @public - * To set the middleware chain - * @param {Middleware[]} middlewareArray - The array containing the middlewares - */ - public setMiddlewareArray(middlewareArray: Middleware[]) { - for (let num = 0; num < middlewareArray.length - 1; num += 1) { - middlewareArray[num].setNext(middlewareArray[num + 1]); - } - this.middleware = middlewareArray[0]; - } - /** * @public * @async diff --git a/src/middleware/AuthenticationHandler.ts b/src/middleware/AuthenticationHandler.ts index 358d900f9..3275c6092 100644 --- a/src/middleware/AuthenticationHandler.ts +++ b/src/middleware/AuthenticationHandler.ts @@ -94,13 +94,4 @@ export class AuthenticationHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } - - /** - * @public - * To get the next middleware in the chain - * @returns next Middleware instance - */ - public getNext(): Middleware { - return this.nextMiddleware; - } } diff --git a/src/middleware/ChaosHandler.ts b/src/middleware/ChaosHandler.ts index bc0ec53c1..c79252cd0 100644 --- a/src/middleware/ChaosHandler.ts +++ b/src/middleware/ChaosHandler.ts @@ -279,13 +279,4 @@ export class ChaosHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } - - /** - * @public - * To get the next middleware in the chain - * @returns next Middleware instance - */ - public getNext(): Middleware { - return this.nextMiddleware; - } } diff --git a/src/middleware/IMiddleware.ts b/src/middleware/IMiddleware.ts index 51e0821d0..70dacc703 100644 --- a/src/middleware/IMiddleware.ts +++ b/src/middleware/IMiddleware.ts @@ -15,5 +15,4 @@ import { Context } from "../IContext"; export interface Middleware { execute: (context: Context) => Promise; setNext?: (middleware: Middleware) => void; - getNext?: () => Middleware; } diff --git a/src/middleware/RedirectHandler.ts b/src/middleware/RedirectHandler.ts index 5878062e5..4bdcc9e39 100644 --- a/src/middleware/RedirectHandler.ts +++ b/src/middleware/RedirectHandler.ts @@ -243,13 +243,4 @@ export class RedirectHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } - - /** - * @public - * To get the next middleware in the chain - * @returns next Middleware instance - */ - public getNext(): Middleware { - return this.nextMiddleware; - } } diff --git a/src/middleware/RetryHandler.ts b/src/middleware/RetryHandler.ts index efd159a26..56b5c22c3 100644 --- a/src/middleware/RetryHandler.ts +++ b/src/middleware/RetryHandler.ts @@ -216,13 +216,4 @@ export class RetryHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } - - /** - * @public - * To get the next middleware in the chain - * @returns next Middleware instance - */ - public getNext(): Middleware { - return this.nextMiddleware; - } } diff --git a/src/middleware/TelemetryHandler.ts b/src/middleware/TelemetryHandler.ts index 1d1b09dec..e72d2c187 100644 --- a/src/middleware/TelemetryHandler.ts +++ b/src/middleware/TelemetryHandler.ts @@ -96,13 +96,4 @@ export class TelemetryHandler implements Middleware { public setNext(next: Middleware): void { this.nextMiddleware = next; } - - /** - * @public - * To get the next middleware in the chain - * @returns next Middleware instance - */ - public getNext(): Middleware { - return this.nextMiddleware; - } } From 183cf629ac6384b30e0f1efdfb7a762f55fc833c Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Tue, 9 Jun 2020 10:19:22 +0430 Subject: [PATCH 15/55] Update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cc7ee95f5..fa90705b5 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,8 @@ npm install msal@ ```typescript import { UserAgentApplication } from "msal"; -import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; +import { ImplicitMSALAuthenticationProvider } from "@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; +import { MSALAuthenticationProviderOptions }from '@microsoft/microsoft-graph-client/lib/src/MSALAuthenticationProviderOptions'; // An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options const msalConfig = { @@ -110,7 +111,7 @@ const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes // Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal // Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication const msalApplication = new UserAgentApplication(msalConfig); -const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const options = new MSALAuthenticationProviderOptions(graphScopes); const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); ``` From 4d59843b54140e699b2ed99c65dff09ff544b437 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Tue, 9 Jun 2020 10:19:41 +0430 Subject: [PATCH 16/55] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa90705b5..94c25c7a5 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ npm install msal@ import { UserAgentApplication } from "msal"; import { ImplicitMSALAuthenticationProvider } from "@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; -import { MSALAuthenticationProviderOptions }from '@microsoft/microsoft-graph-client/lib/src/MSALAuthenticationProviderOptions'; +import { MSALAuthenticationProviderOptions } from '@microsoft/microsoft-graph-client/lib/src/MSALAuthenticationProviderOptions'; // An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options const msalConfig = { From 42ee7aa6c81a6a5af4ca77170693e057fdc0da75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2020 16:50:37 +0000 Subject: [PATCH 17/55] Bump lodash from 4.17.15 to 4.17.19 in /samples/browser Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] --- samples/browser/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/browser/package-lock.json b/samples/browser/package-lock.json index ba9e1a6af..3d6b9d2b4 100644 --- a/samples/browser/package-lock.json +++ b/samples/browser/package-lock.json @@ -479,9 +479,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "mime": { From 6e890cc98e74a4e27f98acac4d054c7146f90bc4 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Mon, 27 Jul 2020 17:04:30 -0700 Subject: [PATCH 18/55] Changed parsepath and query functions to split query params on first equals sign considering nested queries. Added comments to query functions --- spec/core/urlGeneration.ts | 22 +++++++++++++++++++++ spec/core/urlParsing.ts | 8 +++++++- src/GraphRequest.ts | 40 +++++++++++++++++++------------------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/spec/core/urlGeneration.ts b/spec/core/urlGeneration.ts index 830b52d90..34282933e 100644 --- a/spec/core/urlGeneration.ts +++ b/spec/core/urlGeneration.ts @@ -91,6 +91,28 @@ cases.push({ .query("$search=senior"), }); +cases.push({ + url: "https://graph.microsoft.com/beta/me/people?$select=displayName,title,id&$count=false&$expand=a($expand=a,b)", + request: client + .api("/me/people") + .version("beta") + .select(["displayName", "title"]) + .count(true) + .expand("a($expand=a,b)") + .query("$select=id") + .query("$count=false"), +}); + +cases.push({ + url: "https://graph.microsoft.com/v1.0/me/people?$select=displayName,title,id&select=value", + request: client + .api("/me/people") + .version("v1.0") + .select(["displayName", "title"]) + .query({ select: "value" }) + .query({ $select: "id" }), +}); + cases.push({ url: "https://graph.microsoft.com/v1.0/me/drive/root?$expand=children($select=name),permissions", request: client diff --git a/spec/core/urlParsing.ts b/spec/core/urlParsing.ts index c25f4c797..e34f01bd5 100644 --- a/spec/core/urlParsing.ts +++ b/spec/core/urlParsing.ts @@ -28,6 +28,12 @@ const testCases = { "me?$select=displayName": "https://graph.microsoft.com/v1.0/me?$select=displayName", "me?select=displayName": "https://graph.microsoft.com/v1.0/me?select=displayName", "https://graph.microsoft.com/beta/me?select=displayName": "https://graph.microsoft.com/beta/me?select=displayName", + + // test for nested query parameters + "https://graph.microsoft.com/beta/identityGovernance/entitlementManagement/accessPackages/?$expand=accessPackageAssignmentPolicies,accessPackageResourceRoleScopes($expand=accessPackageResourceRole,accessPackageResourceScope)": "https://graph.microsoft.com/beta/identityGovernance/entitlementManagement/accessPackages/?$expand=accessPackageAssignmentPolicies,accessPackageResourceRoleScopes($expand=accessPackageResourceRole,accessPackageResourceScope)", + "me?$select=displayName&$select=id": "https://graph.microsoft.com/v1.0/me?$select=displayName,id", + "/me?$filter=b&$filter=a": "https://graph.microsoft.com/v1.0/me?$filter=a", + "https://graph.microsoft.com/v1.0/me?$top=4&$expand=4&$iscount=true&$top=2": "https://graph.microsoft.com/v1.0/me?$top=2&$expand=4&$iscount=true", }; describe("urlParsing.ts", () => { @@ -42,5 +48,5 @@ describe("urlParsing.ts", () => { } } }); - /* tslint:enable: no-string-literal */ }); +/* tslint:enable: no-string-literal */ diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 366ed1782..1173ab88b 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -170,14 +170,7 @@ export class GraphRequest { // Capture query string into oDataQueryParams and otherURLQueryParams const queryParams = path.substring(queryStrPos + 1, path.length).split("&"); for (const queryParam of queryParams) { - const qParams = queryParam.split("="); - const key = qParams[0]; - const value = qParams[1]; - if (oDataQueryNames.indexOf(key) !== -1) { - this.urlComponents.oDataQueryParams[key] = value; - } else { - this.urlComponents.otherURLQueryParams[key] = value; - } + this.query(queryParam); } } }; @@ -430,7 +423,7 @@ export class GraphRequest { /** * @public - * To add query string for filter OData Query param + * To add query string for filter OData Query param. The request URL accepts only one $filter Odata Query option and it's value is set to the most recently passed filter query string. * @param {string} filterStr - The filter query string * @returns The same GraphRequest instance that is being called with */ @@ -441,7 +434,7 @@ export class GraphRequest { /** * @public - * To add criterion for search OData Query param + * To add criterion for search OData Query param. The request URL accepts only one $search Odata Query option and it's value is set to the most recently passed search criterion string. * @param {string} searchStr - The search criterion string * @returns The same GraphRequest instance that is being called with */ @@ -452,7 +445,7 @@ export class GraphRequest { /** * @public - * To add number for top OData Query param + * To add number for top OData Query param. The request URL accepts only one $top Odata Query option and it's value is set to the most recently passed number value. * @param {number} n - The number value * @returns The same GraphRequest instance that is being called with */ @@ -463,7 +456,7 @@ export class GraphRequest { /** * @public - * To add number for skip OData Query param + * To add number for skip OData Query param. The request URL accepts only one $skip Odata Query option and it's value is set to the most recently passed number value. * @param {number} n - The number value * @returns The same GraphRequest instance that is being called with */ @@ -474,7 +467,7 @@ export class GraphRequest { /** * @public - * To add token string for skipToken OData Query param + * To add token string for skipToken OData Query param. The request URL accepts only one $skipToken Odata Query option and it's value is set to the most recently passed token value. * @param {string} token - The token value * @returns The same GraphRequest instance that is being called with */ @@ -485,7 +478,7 @@ export class GraphRequest { /** * @public - * To add boolean for count OData Query param + * To add boolean for count OData Query param. The URL accepts only one $count Odata Query option and it's value is set to the most recently passed boolean value. * @param {boolean} isCount - The count boolean * @returns The same GraphRequest instance that is being called with */ @@ -501,19 +494,26 @@ export class GraphRequest { * @returns The same GraphRequest instance that is being called with */ public query(queryDictionaryOrString: string | KeyValuePairObjectStringNumber): GraphRequest { - const otherURLQueryParams = this.urlComponents.otherURLQueryParams; + let paramKey: string; + let paramValue: string | number; if (typeof queryDictionaryOrString === "string") { - const querySplit = queryDictionaryOrString.split("="); - const queryKey = querySplit[0]; - const queryValue = querySplit[1]; - otherURLQueryParams[queryKey] = queryValue; + const indexOfFirstEquals = queryDictionaryOrString.indexOf("="); // The query key-value pair must be split on the first equals sign to avoid error while parsing nested query parameters + const qParams = [queryDictionaryOrString.substring(0, indexOfFirstEquals), queryDictionaryOrString.substring(indexOfFirstEquals + 1, queryDictionaryOrString.length)]; + paramKey = qParams[0]; + paramValue = qParams[1]; } else { for (const key in queryDictionaryOrString) { if (queryDictionaryOrString.hasOwnProperty(key)) { - otherURLQueryParams[key] = queryDictionaryOrString[key]; + paramKey = key; + paramValue = queryDictionaryOrString[key]; } } } + if (oDataQueryNames.indexOf(paramKey) !== -1) { + this.urlComponents.oDataQueryParams[paramKey] = paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby" ? (this.urlComponents.oDataQueryParams[paramKey] ? this.urlComponents.oDataQueryParams[paramKey] + "," + paramValue : paramValue) : paramValue; + } else { + this.urlComponents.otherURLQueryParams[paramKey] = paramValue; + } return this; } From 681313941db52980a7b2b17544fc71a7a272b734 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 28 Jul 2020 20:20:32 -0700 Subject: [PATCH 19/55] Correcting spelling in comments, setting the param key directlyand replacing ternary condition with if else --- src/GraphRequest.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 1173ab88b..54aac8329 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -423,7 +423,7 @@ export class GraphRequest { /** * @public - * To add query string for filter OData Query param. The request URL accepts only one $filter Odata Query option and it's value is set to the most recently passed filter query string. + * To add query string for filter OData Query param. The request URL accepts only one $filter Odata Query option and its value is set to the most recently passed filter query string. * @param {string} filterStr - The filter query string * @returns The same GraphRequest instance that is being called with */ @@ -434,7 +434,7 @@ export class GraphRequest { /** * @public - * To add criterion for search OData Query param. The request URL accepts only one $search Odata Query option and it's value is set to the most recently passed search criterion string. + * To add criterion for search OData Query param. The request URL accepts only one $search Odata Query option and its value is set to the most recently passed search criterion string. * @param {string} searchStr - The search criterion string * @returns The same GraphRequest instance that is being called with */ @@ -445,7 +445,7 @@ export class GraphRequest { /** * @public - * To add number for top OData Query param. The request URL accepts only one $top Odata Query option and it's value is set to the most recently passed number value. + * To add number for top OData Query param. The request URL accepts only one $top Odata Query option and its value is set to the most recently passed number value. * @param {number} n - The number value * @returns The same GraphRequest instance that is being called with */ @@ -456,7 +456,7 @@ export class GraphRequest { /** * @public - * To add number for skip OData Query param. The request URL accepts only one $skip Odata Query option and it's value is set to the most recently passed number value. + * To add number for skip OData Query param. The request URL accepts only one $skip Odata Query option and its value is set to the most recently passed number value. * @param {number} n - The number value * @returns The same GraphRequest instance that is being called with */ @@ -467,7 +467,7 @@ export class GraphRequest { /** * @public - * To add token string for skipToken OData Query param. The request URL accepts only one $skipToken Odata Query option and it's value is set to the most recently passed token value. + * To add token string for skipToken OData Query param. The request URL accepts only one $skipToken Odata Query option and its value is set to the most recently passed token value. * @param {string} token - The token value * @returns The same GraphRequest instance that is being called with */ @@ -478,7 +478,7 @@ export class GraphRequest { /** * @public - * To add boolean for count OData Query param. The URL accepts only one $count Odata Query option and it's value is set to the most recently passed boolean value. + * To add boolean for count OData Query param. The URL accepts only one $count Odata Query option and its value is set to the most recently passed boolean value. * @param {boolean} isCount - The count boolean * @returns The same GraphRequest instance that is being called with */ @@ -497,10 +497,11 @@ export class GraphRequest { let paramKey: string; let paramValue: string | number; if (typeof queryDictionaryOrString === "string") { - const indexOfFirstEquals = queryDictionaryOrString.indexOf("="); // The query key-value pair must be split on the first equals sign to avoid error while parsing nested query parameters - const qParams = [queryDictionaryOrString.substring(0, indexOfFirstEquals), queryDictionaryOrString.substring(indexOfFirstEquals + 1, queryDictionaryOrString.length)]; - paramKey = qParams[0]; - paramValue = qParams[1]; + /* The query key-value pair must be split on the first equals sign to avoid errors in parsing nested query parameters. + Example-> "/me?$expand=home($select=city)" */ + const indexOfFirstEquals = queryDictionaryOrString.indexOf("="); + paramKey = queryDictionaryOrString.substring(0, indexOfFirstEquals); + paramValue = queryDictionaryOrString.substring(indexOfFirstEquals + 1, queryDictionaryOrString.length); } else { for (const key in queryDictionaryOrString) { if (queryDictionaryOrString.hasOwnProperty(key)) { @@ -510,7 +511,9 @@ export class GraphRequest { } } if (oDataQueryNames.indexOf(paramKey) !== -1) { - this.urlComponents.oDataQueryParams[paramKey] = paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby" ? (this.urlComponents.oDataQueryParams[paramKey] ? this.urlComponents.oDataQueryParams[paramKey] + "," + paramValue : paramValue) : paramValue; + const currentValue = this.urlComponents.oDataQueryParams[paramKey]; + const isValueAppendable = (paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby") && currentValue; + this.urlComponents.oDataQueryParams[paramKey] = isValueAppendable ? currentValue + "," + paramValue : paramValue; } else { this.urlComponents.otherURLQueryParams[paramKey] = paramValue; } From 72adfd0f193221307eb6ba12ab2ffadd87eb6d5e Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 28 Jul 2020 20:54:59 -0700 Subject: [PATCH 20/55] Adding a buildheaders function to conditionally set content type and checking if body is null when serializing content --- src/GraphRequest.ts | 34 ++++++++++++++++++++++++++++------ src/GraphRequestUtil.ts | 2 +- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 366ed1782..960ed0ae7 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -302,6 +302,28 @@ export class GraphRequest { } } + /** + * @private + * Checks if the content-type is present in the _headers property. If not present, defaults the content-type to application/json + * @param none + * @returns nothing + */ + private buildHeaders(): void { + if (this._headers === undefined || this._headers === null) { + this.header("Content-Type", "application/json"); + } + let isContentTypePresent = false; + const headerKeys = Object.keys(this._headers); + for (const headerKey of headerKeys) { + if (headerKey.toLowerCase() === "content-type") { + isContentTypePresent = true; + } + } + if (!isContentTypePresent) { + this.header("Content-Type", "application/json"); + } + } + /** * @public * Sets the custom header for a request @@ -550,13 +572,13 @@ export class GraphRequest { const options: FetchOptions = { method: RequestMethod.POST, body: serializeContent(content), - headers: - typeof FormData !== "undefined" && content instanceof FormData - ? {} - : { - "Content-Type": "application/json", - }, }; + if (typeof FormData !== "undefined" && content instanceof FormData) { + options.headers = {}; + } else { + this.buildHeaders(); + options.headers = this._headers; + } try { const response = await this.send(url, options, callback); return response; diff --git a/src/GraphRequestUtil.ts b/src/GraphRequestUtil.ts index c2afee5a5..3d645ce6d 100644 --- a/src/GraphRequestUtil.ts +++ b/src/GraphRequestUtil.ts @@ -41,7 +41,7 @@ export const urlJoin = (urlSegments: string[]): string => { */ export const serializeContent = (content: any): any => { - const className: string = content === undefined ? undefined : content.constructor.name; + const className: string = content === undefined || content === null ? undefined : content.constructor.name; if (className === "Buffer" || className === "Blob" || className === "File" || className === "FormData" || typeof content === "string") { return content; } From b946955d6c66a1b18adb966c9d4f311ecd1d6816 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Wed, 29 Jul 2020 11:14:15 -0700 Subject: [PATCH 21/55] Altering condition to be optimal Co-authored-by: Mustafa Zengin --- src/GraphRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 54aac8329..5ee291fea 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -512,7 +512,7 @@ export class GraphRequest { } if (oDataQueryNames.indexOf(paramKey) !== -1) { const currentValue = this.urlComponents.oDataQueryParams[paramKey]; - const isValueAppendable = (paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby") && currentValue; + const isValueAppendable = currentValue && (paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby"); this.urlComponents.oDataQueryParams[paramKey] = isValueAppendable ? currentValue + "," + paramValue : paramValue; } else { this.urlComponents.otherURLQueryParams[paramKey] = paramValue; From d5bb8c412ee7b33f0825783f0a875ebe933222c9 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 4 Aug 2020 00:01:29 -0700 Subject: [PATCH 22/55] Adding formdata to the node project, setting the esModuleInterop config as true for allowing import of npm modules, adding unit test for application/xhtml+xml content type post --- package-lock.json | 47 ++++++++++++++++++++++++++-- package.json | 3 +- spec/development/workload/OneNote.ts | 17 ++++++++++ src/GraphRequest.ts | 8 +++-- tsconfig-cjs.json | 3 +- tsconfig-es.json | 5 +-- 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08478c7a0..33ab9e0b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -798,9 +798,9 @@ "dev": true }, "@types/node": { - "version": "12.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.10.tgz", - "integrity": "sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ==", + "version": "12.12.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", + "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==", "dev": true }, "@types/normalize-package-data": { @@ -1082,6 +1082,11 @@ "async-done": "^1.2.2" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1574,6 +1579,14 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", @@ -1823,6 +1836,11 @@ "rimraf": "^2.2.8" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -2300,6 +2318,16 @@ "for-in": "^1.0.1" } }, + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4231,6 +4259,19 @@ "to-regex": "^3.0.2" } }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", diff --git a/package.json b/package.json index 033467a57..fd089fbc6 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,9 @@ "@babel/plugin-transform-runtime": "^7.4.4", "@babel/preset-env": "^7.4.4", "@types/mocha": "^5.2.6", - "@types/node": "^12.0.10", + "@types/node": "^12.12.53", "chai": "^4.2.0", + "form-data": "^3.0.0", "gulp": "^4.0.2", "husky": "^2.2.0", "isomorphic-fetch": "^2.2.1", diff --git a/spec/development/workload/OneNote.ts b/spec/development/workload/OneNote.ts index 4f394a596..8f72fc91a 100644 --- a/spec/development/workload/OneNote.ts +++ b/spec/development/workload/OneNote.ts @@ -85,6 +85,23 @@ describe("OneNote", function() { } }); + it("Create a OneNote page with application/xhtml+xml page content", async () => { + try { + const body = "A page with a block of HTML

This page contains some formatted text.

"; + const json = await client + .api(`/me/onenote/sections/${section.id}/pages`) + .header("content-type", "application/xhtml+xml") + .post(body); + const createdPageFromHTML = json as OnenotePage; + console.log(json); + assert.isDefined(createdPage.id); + assert.isDefined(createdPage.contentUrl); + assert.isUndefined(createdPage["random fake property that should be null"]); + } catch (error) { + throw error; + } + }); + it("create a OneNote page with html page content and file attachment", async () => { try { const formData = new FormData(); diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 960ed0ae7..0ac9e75ed 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -9,6 +9,8 @@ * @module GraphRequest */ +import FormData from "form-data"; + import { GraphError } from "./GraphError"; import { GraphErrorHandler } from "./GraphErrorHandler"; import { oDataQueryNames, serializeContent, urlJoin } from "./GraphRequestUtil"; @@ -22,7 +24,6 @@ import { MiddlewareControl } from "./middleware/MiddlewareControl"; import { MiddlewareOptions } from "./middleware/options/IMiddlewareOptions"; import { RequestMethod } from "./RequestMethod"; import { ResponseType } from "./ResponseType"; - /** * @interface * Signature to representing key value pairs @@ -573,8 +574,9 @@ export class GraphRequest { method: RequestMethod.POST, body: serializeContent(content), }; - if (typeof FormData !== "undefined" && content instanceof FormData) { - options.headers = {}; + const className: string = content === undefined || content === null ? undefined : content.constructor.name; + if (typeof FormData !== "undefined" && className === "FormData") { + options.headers = content.getHeaders(); } else { this.buildHeaders(); options.headers = this._headers; diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json index 3cc1c3494..75cd63e95 100644 --- a/tsconfig-cjs.json +++ b/tsconfig-cjs.json @@ -4,7 +4,8 @@ "module": "commonjs", "target": "es5", "lib": ["dom", "esnext"], - "outDir": "lib" + "outDir": "lib", + "esModuleInterop": true }, "exclude": ["node_modules", "lib", "samples", "spec/development"], "include": ["./src/**/*.ts", "./spec/**/*.ts"] diff --git a/tsconfig-es.json b/tsconfig-es.json index 1a763aefb..1730f1b04 100644 --- a/tsconfig-es.json +++ b/tsconfig-es.json @@ -1,10 +1,11 @@ { "extends": "./tsconfig-base.json", "compilerOptions": { - "module": "es6", + "module": "CommonJS", "target": "es6", "lib": ["dom", "esnext"], - "outDir": "lib/es" + "outDir": "lib/es", + "esModuleInterop": true }, "exclude": ["node_modules", "lib", "samples", "spec/**"], "include": ["./src/**/*.ts"] From 6c68a7505b80dfbcf30ffe0f72198b3e3abf39b6 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 4 Aug 2020 01:50:55 -0700 Subject: [PATCH 23/55] Adding a private query parsing function and more validations --- src/GraphRequest.ts | 107 +++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 25 deletions(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 5ee291fea..f5af5d4fa 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -49,6 +49,7 @@ export interface URLComponents { path?: string; oDataQueryParams: KeyValuePairObjectStringNumber; otherURLQueryParams: KeyValuePairObjectStringNumber; + otherURLQueryStrings: string[]; } /** @@ -118,6 +119,7 @@ export class GraphRequest { version: this.config.defaultVersion, oDataQueryParams: {}, otherURLQueryParams: {}, + otherURLQueryStrings: [], }; this._headers = {}; this._options = {}; @@ -170,7 +172,7 @@ export class GraphRequest { // Capture query string into oDataQueryParams and otherURLQueryParams const queryParams = path.substring(queryStrPos + 1, path.length).split("&"); for (const queryParam of queryParams) { - this.query(queryParam); + this.parseQueryParameter(queryParam); } } }; @@ -237,9 +239,87 @@ export class GraphRequest { } } } + + if (urlComponents.otherURLQueryStrings.length !== 0) { + for (const str of urlComponents.otherURLQueryStrings) { + query.push(str); + } + } return query.length > 0 ? "?" + query.join("&") : ""; } + /** + * @private + * @param queryDictionaryOrString + */ + private parseQueryParameter(queryDictionaryOrString: string | KeyValuePairObjectStringNumber): GraphRequest { + if (typeof queryDictionaryOrString === "string") { + if (queryDictionaryOrString.charAt(0) === "?") { + queryDictionaryOrString = queryDictionaryOrString.substring(1, queryDictionaryOrString.length); + } + + if (queryDictionaryOrString.indexOf("&") !== -1) { + const queryParams = queryDictionaryOrString.split("&"); + for (const str of queryParams) { + this.parseQueryParamenterString(str); + } + } else { + this.parseQueryParamenterString(queryDictionaryOrString); + } + } else { + for (const key in queryDictionaryOrString) { + if (queryDictionaryOrString.hasOwnProperty(key)) { + this.setURLComponentsQueryParamater(key, queryDictionaryOrString[key]); + } + } + } + + return this; + } + + /** + * @private + * @param query + */ + private parseQueryParamenterString(queryParameter: string): void { + /* The query key-value pair must be split on the first equals sign to avoid errors in parsing nested query parameters. + Example-> "/me?$expand=home($select=city)" */ + if (this.isValidQueryKeyValuePair(queryParameter)) { + const indexOfFirstEquals = queryParameter.indexOf("="); + const paramKey = queryParameter.substring(0, indexOfFirstEquals); + const paramValue = queryParameter.substring(indexOfFirstEquals + 1, queryParameter.length); + this.setURLComponentsQueryParamater(paramKey, paramValue); + } else { + this.urlComponents.otherURLQueryStrings.push(queryParameter); + } + } + + /** + * @private + * @param query + */ + private setURLComponentsQueryParamater(paramKey: string, paramValue: string | number): void { + if (oDataQueryNames.indexOf(paramKey) !== -1) { + const currentValue = this.urlComponents.oDataQueryParams[paramKey]; + const isValueAppendable = currentValue && (paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby"); + this.urlComponents.oDataQueryParams[paramKey] = isValueAppendable ? currentValue + "," + paramValue : paramValue; + } else { + this.urlComponents.otherURLQueryParams[paramKey] = paramValue; + } + } + + private isValidQueryKeyValuePair(queryDictionaryOrString: string): boolean { + const indexofFirstEquals = queryDictionaryOrString.indexOf("="); + if (indexofFirstEquals === -1) { + return false; + } else { + const indexofOpeningParanthesis = queryDictionaryOrString.indexOf("("); + if (indexofOpeningParanthesis !== -1 && queryDictionaryOrString.indexOf("(") < indexofFirstEquals) { + return false; + } + } + return true; + } /** * @private * Updates the custom headers and options for a request @@ -494,30 +574,7 @@ export class GraphRequest { * @returns The same GraphRequest instance that is being called with */ public query(queryDictionaryOrString: string | KeyValuePairObjectStringNumber): GraphRequest { - let paramKey: string; - let paramValue: string | number; - if (typeof queryDictionaryOrString === "string") { - /* The query key-value pair must be split on the first equals sign to avoid errors in parsing nested query parameters. - Example-> "/me?$expand=home($select=city)" */ - const indexOfFirstEquals = queryDictionaryOrString.indexOf("="); - paramKey = queryDictionaryOrString.substring(0, indexOfFirstEquals); - paramValue = queryDictionaryOrString.substring(indexOfFirstEquals + 1, queryDictionaryOrString.length); - } else { - for (const key in queryDictionaryOrString) { - if (queryDictionaryOrString.hasOwnProperty(key)) { - paramKey = key; - paramValue = queryDictionaryOrString[key]; - } - } - } - if (oDataQueryNames.indexOf(paramKey) !== -1) { - const currentValue = this.urlComponents.oDataQueryParams[paramKey]; - const isValueAppendable = currentValue && (paramKey === "$expand" || paramKey === "$select" || paramKey === "$orderby"); - this.urlComponents.oDataQueryParams[paramKey] = isValueAppendable ? currentValue + "," + paramValue : paramValue; - } else { - this.urlComponents.otherURLQueryParams[paramKey] = paramValue; - } - return this; + return this.parseQueryParameter(queryDictionaryOrString); } /** From 4a6322059bc6b2517c14f26580ebe2dd6a5e8abb Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 4 Aug 2020 18:41:30 -0700 Subject: [PATCH 24/55] Adding tests for url parsing and comments to the private url parsing functions --- spec/core/urlGeneration.ts | 31 +++++++++++++++++++++++ spec/core/urlParsing.ts | 5 ++++ src/GraphRequest.ts | 51 +++++++++++++++++++++++++------------- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/spec/core/urlGeneration.ts b/spec/core/urlGeneration.ts index 34282933e..74ca64ce6 100644 --- a/spec/core/urlGeneration.ts +++ b/spec/core/urlGeneration.ts @@ -113,6 +113,37 @@ cases.push({ .query({ $select: "id" }), }); +// handling an invalid input +cases.push({ + url: "https://graph.microsoft.com/v1.0/me/people?$select=displayName,title&select=value&test", + request: client + .api("/me/people") + .version("v1.0") + .select(["displayName", "title"]) + .query({ select: "value" }) + .query("test"), +}); + +// handling an invalid input +cases.push({ + url: "https://graph.microsoft.com/v1.0/me/people?$expand=address($select=home,$expand=city)&$select=home,displayName,title&select=value&test", + request: client + .api("/me/people?$expand=address($select=home,$expand=city)&$select=home") + .version("v1.0") + .select(["displayName", "title"]) + .query({ select: "value" }) + .query("test"), +}); + +cases.push({ + url: "https://graph.microsoft.com/v1.0/me/people?$expand=home($select=home)&name=test", + request: client.api("/me/people").query("?name=test&$expand=home($select=home)"), +}); +cases.push({ + url: "https://graph.microsoft.com/v1.0/me/people?$expand=home($select=home)&name=test", + request: client.api("/me/people?name=test&$expand=home($select=home)"), +}); + cases.push({ url: "https://graph.microsoft.com/v1.0/me/drive/root?$expand=children($select=name),permissions", request: client diff --git a/spec/core/urlParsing.ts b/spec/core/urlParsing.ts index e34f01bd5..e73dea023 100644 --- a/spec/core/urlParsing.ts +++ b/spec/core/urlParsing.ts @@ -34,6 +34,11 @@ const testCases = { "me?$select=displayName&$select=id": "https://graph.microsoft.com/v1.0/me?$select=displayName,id", "/me?$filter=b&$filter=a": "https://graph.microsoft.com/v1.0/me?$filter=a", "https://graph.microsoft.com/v1.0/me?$top=4&$expand=4&$iscount=true&$top=2": "https://graph.microsoft.com/v1.0/me?$top=2&$expand=4&$iscount=true", + "/items?$expand=fields($select=Title)&$expand=name($select=firstName)": "https://graph.microsoft.com/v1.0/items?$expand=fields($select=Title),name($select=firstName)", + + // Passing invalid parameters + "/me?&test&123": "https://graph.microsoft.com/v1.0/me?&test&123", + "/me?$select($select=name)": "https://graph.microsoft.com/v1.0/me?$select($select=name)", }; describe("urlParsing.ts", () => { diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index f5af5d4fa..d9366a5ae 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -49,7 +49,7 @@ export interface URLComponents { path?: string; oDataQueryParams: KeyValuePairObjectStringNumber; otherURLQueryParams: KeyValuePairObjectStringNumber; - otherURLQueryStrings: string[]; + otherURLQueryOptions: any[]; } /** @@ -119,7 +119,7 @@ export class GraphRequest { version: this.config.defaultVersion, oDataQueryParams: {}, otherURLQueryParams: {}, - otherURLQueryStrings: [], + otherURLQueryOptions: [], }; this._headers = {}; this._options = {}; @@ -240,8 +240,8 @@ export class GraphRequest { } } - if (urlComponents.otherURLQueryStrings.length !== 0) { - for (const str of urlComponents.otherURLQueryStrings) { + if (urlComponents.otherURLQueryOptions.length !== 0) { + for (const str of urlComponents.otherURLQueryOptions) { query.push(str); } } @@ -250,7 +250,9 @@ export class GraphRequest { /** * @private - * @param queryDictionaryOrString + * Parses the query parameters to set the urlComponents property of the GraphRequest object + * @param {string|KeyValuePairObjectStringNumber} queryDictionaryOrString - The query parameter + * @returns The same GraphRequest instance that is being called with */ private parseQueryParameter(queryDictionaryOrString: string | KeyValuePairObjectStringNumber): GraphRequest { if (typeof queryDictionaryOrString === "string") { @@ -266,12 +268,15 @@ export class GraphRequest { } else { this.parseQueryParamenterString(queryDictionaryOrString); } - } else { + } else if (queryDictionaryOrString.constructor === Object) { for (const key in queryDictionaryOrString) { if (queryDictionaryOrString.hasOwnProperty(key)) { this.setURLComponentsQueryParamater(key, queryDictionaryOrString[key]); } } + } else { + /*Push values which are not of key-value structure. + Example-> Handle an invalid input->.query(123) and let the Graph API respond with the error in the URL*/ this.urlComponents.otherURLQueryOptions.push(queryDictionaryOrString); } return this; @@ -279,7 +284,9 @@ export class GraphRequest { /** * @private - * @param query + * Parses the query parameter of string type to set the urlComponents property of the GraphRequest object + * @param {string} queryParameter - the query parameters + * returns nothing */ private parseQueryParamenterString(queryParameter: string): void { /* The query key-value pair must be split on the first equals sign to avoid errors in parsing nested query parameters. @@ -290,13 +297,17 @@ export class GraphRequest { const paramValue = queryParameter.substring(indexOfFirstEquals + 1, queryParameter.length); this.setURLComponentsQueryParamater(paramKey, paramValue); } else { - this.urlComponents.otherURLQueryStrings.push(queryParameter); + /* Push values which are not of key-value structure. + Example-> Handle an invalid input->.query(test), .query($select($select=name)) and let the Graph API respond with the error in the URL*/ + this.urlComponents.otherURLQueryOptions.push(queryParameter); } } /** * @private - * @param query + * Sets values into the urlComponents property of GraphRequest object. + * @param {string} paramKey - the query parameter key + * @param {string} paramValue - the query paramter value */ private setURLComponentsQueryParamater(paramKey: string, paramValue: string | number): void { if (oDataQueryNames.indexOf(paramKey) !== -1) { @@ -307,19 +318,25 @@ export class GraphRequest { this.urlComponents.otherURLQueryParams[paramKey] = paramValue; } } - - private isValidQueryKeyValuePair(queryDictionaryOrString: string): boolean { - const indexofFirstEquals = queryDictionaryOrString.indexOf("="); + /** + * @private + * Check if the query parameter string has a valid key-value structure + * @param {string} queryString - the query parameter string. Example -> "name=value" + * #returns true if the query string has a valid key-value structure else false + */ + private isValidQueryKeyValuePair(queryString: string): boolean { + const indexofFirstEquals = queryString.indexOf("="); if (indexofFirstEquals === -1) { return false; - } else { - const indexofOpeningParanthesis = queryDictionaryOrString.indexOf("("); - if (indexofOpeningParanthesis !== -1 && queryDictionaryOrString.indexOf("(") < indexofFirstEquals) { - return false; - } + } + const indexofOpeningParanthesis = queryString.indexOf("("); + if (indexofOpeningParanthesis !== -1 && queryString.indexOf("(") < indexofFirstEquals) { + // Example -> .query($select($expand=true)); + return false; } return true; } + /** * @private * Updates the custom headers and options for a request From 35c804dbd763daacccf4099c11d897d0aed13294 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 4 Aug 2020 19:21:00 -0700 Subject: [PATCH 25/55] Adding more information in the return comments for functions returning the GraphRequest instance --- src/GraphRequest.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index d9366a5ae..f7f923fa6 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -308,6 +308,7 @@ export class GraphRequest { * Sets values into the urlComponents property of GraphRequest object. * @param {string} paramKey - the query parameter key * @param {string} paramValue - the query paramter value + * @returns nothing */ private setURLComponentsQueryParamater(paramKey: string, paramValue: string | number): void { if (oDataQueryNames.indexOf(paramKey) !== -1) { @@ -483,7 +484,7 @@ export class GraphRequest { * @public * To add properties for select OData Query param * @param {string|string[]} properties - The Properties value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the properties for $select query */ /* * Accepts .select("displayName,birthday") @@ -500,7 +501,7 @@ export class GraphRequest { * @public * To add properties for expand OData Query param * @param {string|string[]} properties - The Properties value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the properties for $expand query */ public expand(properties: string | string[]): GraphRequest { this.addCsvQueryParameter("$expand", properties, arguments); @@ -511,7 +512,7 @@ export class GraphRequest { * @public * To add properties for orderby OData Query param * @param {string|string[]} properties - The Properties value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the properties for $orderby query */ public orderby(properties: string | string[]): GraphRequest { this.addCsvQueryParameter("$orderby", properties, arguments); @@ -522,7 +523,7 @@ export class GraphRequest { * @public * To add query string for filter OData Query param. The request URL accepts only one $filter Odata Query option and its value is set to the most recently passed filter query string. * @param {string} filterStr - The filter query string - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the $filter query */ public filter(filterStr: string): GraphRequest { this.urlComponents.oDataQueryParams.$filter = filterStr; @@ -533,7 +534,7 @@ export class GraphRequest { * @public * To add criterion for search OData Query param. The request URL accepts only one $search Odata Query option and its value is set to the most recently passed search criterion string. * @param {string} searchStr - The search criterion string - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the $search query criteria */ public search(searchStr: string): GraphRequest { this.urlComponents.oDataQueryParams.$search = searchStr; @@ -544,7 +545,7 @@ export class GraphRequest { * @public * To add number for top OData Query param. The request URL accepts only one $top Odata Query option and its value is set to the most recently passed number value. * @param {number} n - The number value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the number for $top query */ public top(n: number): GraphRequest { this.urlComponents.oDataQueryParams.$top = n; @@ -555,7 +556,7 @@ export class GraphRequest { * @public * To add number for skip OData Query param. The request URL accepts only one $skip Odata Query option and its value is set to the most recently passed number value. * @param {number} n - The number value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the number for the $skip query */ public skip(n: number): GraphRequest { this.urlComponents.oDataQueryParams.$skip = n; @@ -566,7 +567,7 @@ export class GraphRequest { * @public * To add token string for skipToken OData Query param. The request URL accepts only one $skipToken Odata Query option and its value is set to the most recently passed token value. * @param {string} token - The token value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the token string for $skipToken query option */ public skipToken(token: string): GraphRequest { this.urlComponents.oDataQueryParams.$skipToken = token; @@ -577,7 +578,7 @@ export class GraphRequest { * @public * To add boolean for count OData Query param. The URL accepts only one $count Odata Query option and its value is set to the most recently passed boolean value. * @param {boolean} isCount - The count boolean - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after adding the boolean value for the $count query option */ public count(isCount: boolean = false): GraphRequest { this.urlComponents.oDataQueryParams.$count = isCount.toString(); @@ -588,7 +589,11 @@ export class GraphRequest { * @public * Appends query string to the urlComponent * @param {string|KeyValuePairObjectStringNumber} queryDictionaryOrString - The query value - * @returns The same GraphRequest instance that is being called with + * @returns The same GraphRequest instance that is being called with, after appending the query string to the url component + */ + /* + * Accepts .query("displayName=xyz") + * and .select({ name: "value" }) */ public query(queryDictionaryOrString: string | KeyValuePairObjectStringNumber): GraphRequest { return this.parseQueryParameter(queryDictionaryOrString); From 099fa024db6baeaab0475886f9ef0572161f83f7 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Tue, 4 Aug 2020 20:56:58 -0700 Subject: [PATCH 26/55] Adding unit test for serializecontent testing if content is null, changed content type setting for put and patch --- spec/core/GraphRequestUtil.ts | 5 +++++ spec/development/workload/OneNote.ts | 1 - src/GraphRequest.ts | 16 +++++++--------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/spec/core/GraphRequestUtil.ts b/spec/core/GraphRequestUtil.ts index 021ef1905..19369d355 100644 --- a/spec/core/GraphRequestUtil.ts +++ b/spec/core/GraphRequestUtil.ts @@ -71,5 +71,10 @@ describe("GraphRequestUtil.ts", () => { const val = undefined; assert.equal(serializeContent(val), undefined); }); + + it("Should return 'null' for the case of null content value", () => { + const val = null; + assert.equal(serializeContent(val), "null"); + }); }); }); diff --git a/spec/development/workload/OneNote.ts b/spec/development/workload/OneNote.ts index 8f72fc91a..46b6b5ff5 100644 --- a/spec/development/workload/OneNote.ts +++ b/spec/development/workload/OneNote.ts @@ -93,7 +93,6 @@ describe("OneNote", function() { .header("content-type", "application/xhtml+xml") .post(body); const createdPageFromHTML = json as OnenotePage; - console.log(json); assert.isDefined(createdPage.id); assert.isDefined(createdPage.contentUrl); assert.isUndefined(createdPage["random fake property that should be null"]); diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 0ac9e75ed..1498ffde8 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -309,7 +309,7 @@ export class GraphRequest { * @param none * @returns nothing */ - private buildHeaders(): void { + private setHeaderContentType(): void { if (this._headers === undefined || this._headers === null) { this.header("Content-Type", "application/json"); } @@ -320,6 +320,7 @@ export class GraphRequest { isContentTypePresent = true; } } + // Default the content-type to application/json in case the content-type is not present in the header if (!isContentTypePresent) { this.header("Content-Type", "application/json"); } @@ -576,9 +577,10 @@ export class GraphRequest { }; const className: string = content === undefined || content === null ? undefined : content.constructor.name; if (typeof FormData !== "undefined" && className === "FormData") { - options.headers = content.getHeaders(); + // Content-Type headers should not be specified in case the of FormData type content + options.headers = {}; } else { - this.buildHeaders(); + this.setHeaderContentType(); options.headers = this._headers; } try { @@ -615,12 +617,10 @@ export class GraphRequest { */ public async put(content: any, callback?: GraphRequestCallback): Promise { const url = this.buildFullUrl(); + this.setHeaderContentType(); const options: FetchOptions = { method: RequestMethod.PUT, body: serializeContent(content), - headers: { - "Content-Type": "application/json", - }, }; try { const response = await this.send(url, options, callback); @@ -640,12 +640,10 @@ export class GraphRequest { */ public async patch(content: any, callback?: GraphRequestCallback): Promise { const url = this.buildFullUrl(); + this.setHeaderContentType(); const options: FetchOptions = { method: RequestMethod.PATCH, body: serializeContent(content), - headers: { - "Content-Type": "application/json", - }, }; try { const response = await this.send(url, options, callback); From 1f342bf11453eb7709902536bc2af116e9982049 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Wed, 5 Aug 2020 11:02:25 -0700 Subject: [PATCH 27/55] Changing the conditional expression to check for undefined or null headers Co-authored-by: Mustafa Zengin --- src/GraphRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 1498ffde8..6aaadf97e 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -310,7 +310,7 @@ export class GraphRequest { * @returns nothing */ private setHeaderContentType(): void { - if (this._headers === undefined || this._headers === null) { + if (!this._headers) { this.header("Content-Type", "application/json"); } let isContentTypePresent = false; From 36b0cc92a750bac43079cf298e75476ff4448cff Mon Sep 17 00:00:00 2001 From: nikithauc Date: Wed, 5 Aug 2020 11:11:07 -0700 Subject: [PATCH 28/55] Adding the missing return Co-authored-by: Mustafa Zengin --- src/GraphRequest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 6aaadf97e..0204def4f 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -312,6 +312,7 @@ export class GraphRequest { private setHeaderContentType(): void { if (!this._headers) { this.header("Content-Type", "application/json"); + return; } let isContentTypePresent = false; const headerKeys = Object.keys(this._headers); From d8a1ff9818c3786b75e6418c62b9661e9dee75d5 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Wed, 5 Aug 2020 11:47:32 -0700 Subject: [PATCH 29/55] Removing the try catch block from the test case --- spec/development/workload/OneNote.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/spec/development/workload/OneNote.ts b/spec/development/workload/OneNote.ts index 46b6b5ff5..6ed20f725 100644 --- a/spec/development/workload/OneNote.ts +++ b/spec/development/workload/OneNote.ts @@ -70,19 +70,15 @@ describe("OneNote", function() { } }); it("Create a OneNote page with html page content", async () => { - try { - const formData = new FormData(); - formData.append("Presentation", fs.createReadStream("./spec/sample_files/onenotepage.html")); - const json = await client.api(`/me/onenote/sections/${section.id}/pages`).post(formData); - const createdPageFromHTML = json as OnenotePage; + const formData = new FormData(); + formData.append("Presentation", fs.createReadStream("./spec/sample_files/onenotepage.html")); + const json = await client.api(`/me/onenote/sections/${section.id}/pages`).post(formData); + const createdPageFromHTML = json as OnenotePage; - assert.isDefined(createdPage.id); - assert.isDefined(createdPage.contentUrl); - assert.equal("New Page", createdPageFromHTML.title); - assert.isUndefined(createdPage["random fake property that should be null"]); - } catch (error) { - throw error; - } + assert.isDefined(createdPage.id); + assert.isDefined(createdPage.contentUrl); + assert.equal("New Page", createdPageFromHTML.title); + assert.isUndefined(createdPage["random fake property that should be null"]); }); it("Create a OneNote page with application/xhtml+xml page content", async () => { From 2b0d381c9d1b2a63c39c239ee53e49b53f83f48e Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Wed, 5 Aug 2020 11:50:59 -0700 Subject: [PATCH 30/55] Removing the try catch block from the test case --- spec/development/workload/OneNote.ts | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/spec/development/workload/OneNote.ts b/spec/development/workload/OneNote.ts index 6ed20f725..87d47e5e3 100644 --- a/spec/development/workload/OneNote.ts +++ b/spec/development/workload/OneNote.ts @@ -82,19 +82,15 @@ describe("OneNote", function() { }); it("Create a OneNote page with application/xhtml+xml page content", async () => { - try { - const body = "A page with a block of HTML

This page contains some formatted text.

"; - const json = await client - .api(`/me/onenote/sections/${section.id}/pages`) - .header("content-type", "application/xhtml+xml") - .post(body); - const createdPageFromHTML = json as OnenotePage; - assert.isDefined(createdPage.id); - assert.isDefined(createdPage.contentUrl); - assert.isUndefined(createdPage["random fake property that should be null"]); - } catch (error) { - throw error; - } + const body = "A page with a block of HTML

This page contains some formatted text.

"; + const json = await client + .api(`/me/onenote/sections/${section.id}/pages`) + .header("content-type", "application/xhtml+xml") + .post(body); + const createdPageFromHTML = json as OnenotePage; + assert.isDefined(createdPage.id); + assert.isDefined(createdPage.contentUrl); + assert.isUndefined(createdPage["random fake property that should be null"]); }); it("create a OneNote page with html page content and file attachment", async () => { From 45727ebba35b6624c6231b0100f6d1c805e78814 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Fri, 7 Aug 2020 13:30:57 -0700 Subject: [PATCH 31/55] Returning if the content-type is present else setting content-type as default --- package-lock.json | 12 +++++++++--- src/GraphRequest.ts | 7 ++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33ab9e0b8..0e2a5215b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1085,7 +1085,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "atob": { "version": "2.1.2", @@ -1583,6 +1584,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -1839,7 +1841,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "detect-file": { "version": "1.0.0", @@ -2322,6 +2325,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4262,12 +4266,14 @@ "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true }, "mime-types": { "version": "2.1.27", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, "requires": { "mime-db": "1.44.0" } diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 0204def4f..c54c3f213 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -314,17 +314,14 @@ export class GraphRequest { this.header("Content-Type", "application/json"); return; } - let isContentTypePresent = false; const headerKeys = Object.keys(this._headers); for (const headerKey of headerKeys) { if (headerKey.toLowerCase() === "content-type") { - isContentTypePresent = true; + return; } } // Default the content-type to application/json in case the content-type is not present in the header - if (!isContentTypePresent) { - this.header("Content-Type", "application/json"); - } + this.header("Content-Type", "application/json"); } /** From 3f9412d2873bf5691c89f1ecbe852e8f0e63adbc Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Wed, 12 Aug 2020 01:23:09 -0700 Subject: [PATCH 32/55] Uninstalling formdata, restoring the tsconfigs and removing the condition to check if form-data is undefined in GraphRequest.ts --- package-lock.json | 47 --------------------------- package.json | 1 - samples/browser/src/secrets.sample.js | 3 -- src/GraphRequest.ts | 4 +-- tsconfig-cjs.json | 3 +- tsconfig-es.json | 5 ++- 6 files changed, 4 insertions(+), 59 deletions(-) delete mode 100644 samples/browser/src/secrets.sample.js diff --git a/package-lock.json b/package-lock.json index 0e2a5215b..81d131e47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1082,12 +1082,6 @@ "async-done": "^1.2.2" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1580,15 +1574,6 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", @@ -1838,12 +1823,6 @@ "rimraf": "^2.2.8" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -2321,17 +2300,6 @@ "for-in": "^1.0.1" } }, - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4263,21 +4231,6 @@ "to-regex": "^3.0.2" } }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "dev": true - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dev": true, - "requires": { - "mime-db": "1.44.0" - } - }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", diff --git a/package.json b/package.json index fd089fbc6..67197dabd 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "@types/mocha": "^5.2.6", "@types/node": "^12.12.53", "chai": "^4.2.0", - "form-data": "^3.0.0", "gulp": "^4.0.2", "husky": "^2.2.0", "isomorphic-fetch": "^2.2.1", diff --git a/samples/browser/src/secrets.sample.js b/samples/browser/src/secrets.sample.js deleted file mode 100644 index 6828163a9..000000000 --- a/samples/browser/src/secrets.sample.js +++ /dev/null @@ -1,3 +0,0 @@ -const Secrets = { - clientId: "", -}; diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index c54c3f213..dee572a9b 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -9,8 +9,6 @@ * @module GraphRequest */ -import FormData from "form-data"; - import { GraphError } from "./GraphError"; import { GraphErrorHandler } from "./GraphErrorHandler"; import { oDataQueryNames, serializeContent, urlJoin } from "./GraphRequestUtil"; @@ -574,7 +572,7 @@ export class GraphRequest { body: serializeContent(content), }; const className: string = content === undefined || content === null ? undefined : content.constructor.name; - if (typeof FormData !== "undefined" && className === "FormData") { + if (className === "FormData") { // Content-Type headers should not be specified in case the of FormData type content options.headers = {}; } else { diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json index 75cd63e95..3cc1c3494 100644 --- a/tsconfig-cjs.json +++ b/tsconfig-cjs.json @@ -4,8 +4,7 @@ "module": "commonjs", "target": "es5", "lib": ["dom", "esnext"], - "outDir": "lib", - "esModuleInterop": true + "outDir": "lib" }, "exclude": ["node_modules", "lib", "samples", "spec/development"], "include": ["./src/**/*.ts", "./spec/**/*.ts"] diff --git a/tsconfig-es.json b/tsconfig-es.json index 1730f1b04..1a763aefb 100644 --- a/tsconfig-es.json +++ b/tsconfig-es.json @@ -1,11 +1,10 @@ { "extends": "./tsconfig-base.json", "compilerOptions": { - "module": "CommonJS", + "module": "es6", "target": "es6", "lib": ["dom", "esnext"], - "outDir": "lib/es", - "esModuleInterop": true + "outDir": "lib/es" }, "exclude": ["node_modules", "lib", "samples", "spec/**"], "include": ["./src/**/*.ts"] From 5dd2a1d8804a33ba3f47b5fff863818d1f1af3a5 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Wed, 12 Aug 2020 01:49:20 -0700 Subject: [PATCH 33/55] Restoring file --- samples/browser/src/secrets.sample.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 samples/browser/src/secrets.sample.js diff --git a/samples/browser/src/secrets.sample.js b/samples/browser/src/secrets.sample.js new file mode 100644 index 000000000..6828163a9 --- /dev/null +++ b/samples/browser/src/secrets.sample.js @@ -0,0 +1,3 @@ +const Secrets = { + clientId: "", +}; From 62b5f8c95dd408e4142281188d0748291aadcbc2 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Wed, 12 Aug 2020 12:34:47 -0700 Subject: [PATCH 34/55] Reverting the package json changes --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81d131e47..08478c7a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -798,9 +798,9 @@ "dev": true }, "@types/node": { - "version": "12.12.53", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", - "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==", + "version": "12.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.10.tgz", + "integrity": "sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ==", "dev": true }, "@types/normalize-package-data": { diff --git a/package.json b/package.json index 67197dabd..033467a57 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@babel/plugin-transform-runtime": "^7.4.4", "@babel/preset-env": "^7.4.4", "@types/mocha": "^5.2.6", - "@types/node": "^12.12.53", + "@types/node": "^12.0.10", "chai": "^4.2.0", "gulp": "^4.0.2", "husky": "^2.2.0", From 31389669ac7013199d6d98c12b3236a06370c5e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2020 23:18:22 -0700 Subject: [PATCH 35/55] Bump yargs-parser from 13.1.1 to 13.1.2 in /samples/browser (#321) Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 13.1.1 to 13.1.2. - [Release notes](https://github.com/yargs/yargs-parser/releases) - [Changelog](https://github.com/yargs/yargs-parser/blob/master/docs/CHANGELOG-full.md) - [Commits](https://github.com/yargs/yargs-parser/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/browser/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/browser/package-lock.json b/samples/browser/package-lock.json index 3d6b9d2b4..c0ee8c627 100644 --- a/samples/browser/package-lock.json +++ b/samples/browser/package-lock.json @@ -1070,9 +1070,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", From 9e25f055361e82baa05c790852a8cae829db3eb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2020 23:28:21 -0700 Subject: [PATCH 36/55] Bump mixin-deep from 1.3.1 to 1.3.2 in /scripts (#301) Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/jonschlinkert/mixin-deep/releases) - [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vincent Biret Co-authored-by: nikithauc --- scripts/package-lock.json | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/package-lock.json b/scripts/package-lock.json index e66812be4..fceb874a9 100644 --- a/scripts/package-lock.json +++ b/scripts/package-lock.json @@ -1172,7 +1172,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1587,7 +1588,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1643,6 +1645,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1686,12 +1689,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -2579,9 +2584,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", From ab824f76942074939d4cbddd1389de8a0589d35b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2020 23:37:37 -0700 Subject: [PATCH 37/55] Bump http-proxy from 1.17.0 to 1.18.1 in /samples/browser (#319) Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/http-party/node-http-proxy/releases) - [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md) - [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/browser/package-lock.json | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/samples/browser/package-lock.json b/samples/browser/package-lock.json index c0ee8c627..08cddce18 100644 --- a/samples/browser/package-lock.json +++ b/samples/browser/package-lock.json @@ -223,12 +223,6 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true - }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -408,14 +402,22 @@ } }, "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { - "eventemitter3": "^3.0.0", + "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + } } }, "inflight": { From aaf142eff2c6a73af8624e0530c27e0bc04f3e59 Mon Sep 17 00:00:00 2001 From: Hari Sridharan Date: Thu, 17 Sep 2020 13:17:41 -0700 Subject: [PATCH 38/55] Update OneDriveLargeFileUploadTask.ts (#325) * Update OneDriveLargeFileUploadTask.ts Trying to upload files with the '#' or '%' character in the filename ends with network errors of the form - 400 status response "The parameter item does not exist in method getByPath" Encoding the entire URL does not encode URL components correctly. When the SDK's client tries to encode these URL components in the parameters on the call to OneDriveLargeFileUploadTask.create, then the resultant file in OneDrive has the URL encoded filename (as opposed to the user-friendly/decoded filename). Therefore, fix the encoding in the SDK's URL components itself, directly. * Update OneDriveLargeFileUploadTask.ts Fix typo * Update src/tasks/OneDriveLargeFileUploadTask.ts Updated comment Co-authored-by: Mustafa Zengin * Update OneDriveLargeFileUploadTask.ts Updated comment Co-authored-by: Mustafa Zengin --- src/tasks/OneDriveLargeFileUploadTask.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tasks/OneDriveLargeFileUploadTask.ts b/src/tasks/OneDriveLargeFileUploadTask.ts index 2716f4a22..7f069ee28 100644 --- a/src/tasks/OneDriveLargeFileUploadTask.ts +++ b/src/tasks/OneDriveLargeFileUploadTask.ts @@ -59,7 +59,9 @@ export class OneDriveLargeFileUploadTask extends LargeFileUploadTask { if (path[path.length - 1] !== "/") { path = `${path}/`; } - return encodeURI(`/me/drive/root:${path}${fileName}:/createUploadSession`); + // we choose to encode each component of the file path separately because when encoding full URI + // with encodeURI, special characters like # or % in the file name doesn't get encoded as desired + return `/me/drive/root:${path.split('/').map(p => encodeURIComponent(p)).join('/')}${encodeURIComponent(fileName)}:/createUploadSession`; } /** From a2742b31fea2efc42af8e4f0e83ec98c19aa2c1d Mon Sep 17 00:00:00 2001 From: mattdenkers Date: Thu, 24 Sep 2020 02:57:50 -0600 Subject: [PATCH 39/55] Use correct class name (#284) Co-authored-by: Vincent Biret Co-authored-by: nikithauc --- docs/CustomAuthenticationProvider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CustomAuthenticationProvider.md b/docs/CustomAuthenticationProvider.md index 625adf944..71a26354e 100644 --- a/docs/CustomAuthenticationProvider.md +++ b/docs/CustomAuthenticationProvider.md @@ -30,7 +30,7 @@ Pass instance of MyAuthenticationProvider while initializing. import { MyAuthenticationProvider } from "./MyAuthenticationProvider"; let clientOptions: ClientOptions = { - authProvider: new MyCustomAuthenticationProvider(), + authProvider: new MyAuthenticationProvider(), }; const client = Client.initWithMiddleware(clientOptions); ``` From 8089cd0c15c4269f0c3bac10fd4e291862138949 Mon Sep 17 00:00:00 2001 From: lewgordon <50742795+lewgordon@users.noreply.github.com> Date: Thu, 24 Sep 2020 05:05:47 -0400 Subject: [PATCH 40/55] Typo in documentation example. (#270) Co-authored-by: Vincent Biret Co-authored-by: nikithauc --- docs/Actions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Actions.md b/docs/Actions.md index 4f901b71b..701f21b2d 100644 --- a/docs/Actions.md +++ b/docs/Actions.md @@ -29,13 +29,13 @@ client .then((stream) => { let writeStream = fs.createWriteStream(`../`); // Eg: test.pdf stream.pipe(writeStream).on("error", (err) => { - throw error; + throw err; }); writeStream.on("finish", () => { console.log("Downloaded"); }); writeStream.on("error", (err) => { - throw error; + throw err; }); }) .catch((error) => { From 130f3c0e9d329498ef8444d86181255b640d5743 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Fri, 25 Sep 2020 14:34:26 -0700 Subject: [PATCH 41/55] Enhancement/#311 page iterator request options (#318) * Adding request options property to PageIterator * Adding tests for page iterator task to test passing along the headers * using headersinit type for headers * Specifying parameter definition * using constants * Updating function documentation * remove response type, test passing fetchoptions * testing requestOptions set in pageiterator * typos, optional parameters comments --- spec/development/workload/PageIterator.ts | 123 ++++++++++++++++++ src/GraphRequest.ts | 8 +- src/middleware/ChaosHandler.ts | 16 ++- src/middleware/options/ChaosHandlerOptions.ts | 18 ++- src/tasks/PageIterator.ts | 40 +++++- 5 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 spec/development/workload/PageIterator.ts diff --git a/spec/development/workload/PageIterator.ts b/spec/development/workload/PageIterator.ts new file mode 100644 index 000000000..12e9063e1 --- /dev/null +++ b/spec/development/workload/PageIterator.ts @@ -0,0 +1,123 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +import { assert } from "chai"; +import { Event } from "microsoft-graph"; + +import { PageIterator, PageIteratorCallback, GraphRequestOptions, PageCollection } from "../../../src/tasks/PageIterator"; +import { getClient } from "../test-helper"; +import { ChaosHandler } from "../../../src/middleware/ChaosHandler"; +import { ChaosHandlerOptions } from "../../../src/middleware/options/ChaosHandlerOptions"; +import { ChaosStrategy } from "../../../src/middleware/options/ChaosStrategy"; +import { Client, ClientOptions } from "../../../src"; + +const client = getClient(); +describe("PageIterator", function() { + const pstHeader = { Prefer: 'outlook.timezone= "pacific standard time"' }; + const utc = "UTC"; + const pst = "Pacific Standard Time"; + const testURL = "/me/events"; + + before(async function() { + this.timeout(20000); + + const response = await client.api(testURL).get(); + const numberOfEvents = 4; + const existingEventsCount = response.value.length; + + if (existingEventsCount >= numberOfEvents) { + return; + } + const eventSubject = '"subject": "Test event '; + const eventTimeZone = '"timeZone": "UTC"'; + const eventStartDateTime = '"start": { "dateTime":"' + new Date().toISOString() + '",' + eventTimeZone + "}"; + const eventEndDateTime = '"end": { "dateTime":"' + new Date().toISOString() + '",' + eventTimeZone + "}"; + + for (let i = 1; i <= numberOfEvents - existingEventsCount; i++) { + const eventBody = "{" + eventSubject + "" + 1 + '",' + eventStartDateTime + "," + eventEndDateTime + "}"; + const response = await client.api(testURL).post(eventBody); + if (response.error) { + throw response.error; + } + } + }); + + it("same headers passed with pageIterator", async () => { + const response = await client + .api(`${testURL}?$top=2`) + .headers(pstHeader) + .select("id,start,end") + .get(); + + const callback: PageIteratorCallback = (eventResponse) => { + const event = eventResponse as Event; + assert.equal(event.start.timeZone, pst); + return true; + }; + var requestOptions: GraphRequestOptions = { options: { headers: pstHeader } }; + if (response["@odata.nextLink"]) { + const pageIterator = new PageIterator(client, response, callback, requestOptions); + await pageIterator.iterate(); + assert.isTrue(pageIterator.isComplete()); + } + }).timeout(30 * 1000); + + it("different headers passed with pageIterator", async () => { + const response = await client + .api(`${testURL}?$top=2`) + .headers({ Prefer: `outlook.timezone= "${utc}"` }) + .select("id,start,end") + .get(); + + let counter = 0; + const callback: PageIteratorCallback = (eventResponse) => { + const event = eventResponse as Event; + if (counter < 2) { + assert.equal(event.start.timeZone, utc); + counter++; + } else { + assert.equal(event.start.timeZone, pst); + } + return true; + }; + + var requestOptions = { headers: pstHeader }; + if (response["@odata.nextLink"]) { + const pageIterator = new PageIterator(client, response, callback, requestOptions); + await pageIterator.iterate(); + assert.isTrue(pageIterator.isComplete()); + } + }).timeout(30 * 1000); + + it("setting middleware with pageIterator", async () => { + const middleware = new ChaosHandler(); + const getPageCollection = () => { + return { + value: [], + "@odata.nextLink": "nextURL", + additionalContent: "additional content", + }; + }; + const clientOptions: ClientOptions = { + middleware, + }; + const responseBody = { value: [{ event1: "value1" }, { event2: "value2" }] }; + let counter = 1; + const callback: PageIteratorCallback = (data) => { + assert.equal(data["event" + counter], "value" + counter); + counter++; + return true; + }; + + const middlewareOptions = [new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200, "middleware options for pageIterator", 0, responseBody)]; + const requestOptions = { middlewareOptions }; + + const client = Client.initWithMiddleware(clientOptions); + const pageIterator = new PageIterator(client, getPageCollection(), callback, requestOptions); + await pageIterator.iterate(); + }); +}); diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index c83f22399..237266359 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -79,9 +79,7 @@ export class GraphRequest { * @private * A member to hold custom header options for a request */ - private _headers: { - [key: string]: string; - }; + private _headers: HeadersInit; /** * @private @@ -428,10 +426,10 @@ export class GraphRequest { /** * @public * Sets the custom headers for a request - * @param {KeyValuePairObjectStringNumber} headers - The headers key value pair object + * @param {KeyValuePairObjectStringNumber | HeadersInit} headers - The request headers * @returns The same GraphRequest instance that is being called with */ - public headers(headers: KeyValuePairObjectStringNumber): GraphRequest { + public headers(headers: KeyValuePairObjectStringNumber | HeadersInit): GraphRequest { for (const key in headers) { if (headers.hasOwnProperty(key)) { this._headers[key] = headers[key] as string; diff --git a/src/middleware/ChaosHandler.ts b/src/middleware/ChaosHandler.ts index c79252cd0..5944cad79 100644 --- a/src/middleware/ChaosHandler.ts +++ b/src/middleware/ChaosHandler.ts @@ -91,15 +91,19 @@ export class ChaosHandler implements Middleware { * @param {string} statusMessage - the status message to be returned for the request * @param {string} requestID - request id * @param {string} requestDate - date of the request + * @param {any?} requestBody - the request body to be returned for the request * @returns response body */ - private createResponseBody(statusCode: number, statusMessage: string, requestID: string, requestDate: string) { - let responseBody: any; + private createResponseBody(statusCode: number, statusMessage: string, requestID: string, requestDate: string, responseBody?: any) { + if (responseBody) { + return responseBody; + } + let body: any; if (statusCode >= 400) { const codeMessage: string = httpStatusCode[statusCode]; const errMessage: string = statusMessage; - responseBody = { + body = { error: { code: codeMessage, message: errMessage, @@ -110,9 +114,9 @@ export class ChaosHandler implements Middleware { }, }; } else { - responseBody = {}; + body = {}; } - return responseBody; + return body; } /** @@ -132,7 +136,7 @@ export class ChaosHandler implements Middleware { requestID = generateUUID(); requestDate = new Date(); responseHeader = this.createResponseHeaders(chaosHandlerOptions.statusCode, requestID, requestDate.toString()); - responseBody = this.createResponseBody(chaosHandlerOptions.statusCode, chaosHandlerOptions.statusMessage, requestID, requestDate.toString()); + responseBody = this.createResponseBody(chaosHandlerOptions.statusCode, chaosHandlerOptions.statusMessage, requestID, requestDate.toString(), chaosHandlerOptions.responseBody); const init: any = { url: requestURL, status: chaosHandlerOptions.statusCode, statusText: chaosHandlerOptions.statusMessage, headers: responseHeader }; context.response = new Response(responseBody, init); } catch (error) { diff --git a/src/middleware/options/ChaosHandlerOptions.ts b/src/middleware/options/ChaosHandlerOptions.ts index c74fe76cb..1f3c0f6b1 100644 --- a/src/middleware/options/ChaosHandlerOptions.ts +++ b/src/middleware/options/ChaosHandlerOptions.ts @@ -20,7 +20,7 @@ import { MiddlewareOptions } from "./IMiddlewareOptions"; */ export class ChaosHandlerOptions implements MiddlewareOptions { /** - * Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL + * Specifies the startegy used for the Testing Handler -> RANDOM/MANUAL * * @public */ @@ -48,20 +48,30 @@ export class ChaosHandlerOptions implements MiddlewareOptions { */ public chaosPercentage: number; + /** + * The response body to be returned in the response + * + * @public + */ + public responseBody: any; + /** * @public * @constructor * To create an instance of Testing Handler Options * @param {ChaosStrategy} ChaosStrategy - Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL - * @param {number?} statusCode - The Message to be returned in the response - * @param {string} - The Message to be returned in the response + * @param {number?} statusCode - The statusCode to be returned in the response + * @param {string} statusMessage - The Message to be returned in the response + * @param {number?} chaosPercentage - The percentage of randomness/chaos in the handler + * @param {any?} responseBody - The response body to be returned in the response * @returns An instance of ChaosHandlerOptions */ - public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusCode?: number, statusMessage: string = "Some error Happened", chaosPercentage?: number) { + public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusCode?: number, statusMessage: string = "Some error Happened", chaosPercentage?: number, responseBody?: any) { this.chaosStrategy = chaosStrategy; this.statusCode = statusCode; this.statusMessage = statusMessage; this.chaosPercentage = chaosPercentage !== undefined ? chaosPercentage : 10; + this.responseBody = responseBody; if (this.chaosPercentage > 100) { throw new Error("Error Pecentage can not be more than 100"); } diff --git a/src/tasks/PageIterator.ts b/src/tasks/PageIterator.ts index 3fa90e4f8..b23cc68a1 100644 --- a/src/tasks/PageIterator.ts +++ b/src/tasks/PageIterator.ts @@ -9,7 +9,10 @@ * @module PageIterator */ +import { FetchOptions } from "../IFetchOptions"; import { Client } from "../index"; +import { MiddlewareOptions } from "../middleware/options/IMiddlewareOptions"; +import { ResponseType } from "../ResponseType"; /** * Signature representing PageCollection @@ -25,6 +28,19 @@ export interface PageCollection { [Key: string]: any; } +/** + * Signature to define the request options to be sent during request. + * The values of the GraphRequestOptions properties are passed to the Graph Request object. + * @property {HeadersInit} headers - the header options for the request + * @property {MiddlewareOptions[]} middlewareoptions - The middleware options for the request + * @property {FetchOptions} options - The fetch options for the request + */ +export interface GraphRequestOptions { + headers?: HeadersInit; + middlewareOptions?: MiddlewareOptions[]; + options?: FetchOptions; +} + /** * Signature representing callback for page iterator * @property {Function} callback - The callback function which should return boolean to continue the continue/stop the iteration. @@ -73,6 +89,11 @@ export class PageIterator { */ private complete: boolean; + /** + * Information to be added to the request + */ + private requestOptions: GraphRequestOptions; + /** * @public * @constructor @@ -80,15 +101,17 @@ export class PageIterator { * @param {Client} client - The graph client instance * @param {PageCollection} pageCollection - The page collection object * @param {PageIteratorCallback} callBack - The callback function + * @param {GraphRequestOptions} requestOptions - The request options * @returns An instance of a PageIterator */ - public constructor(client: Client, pageCollection: PageCollection, callback: PageIteratorCallback) { + public constructor(client: Client, pageCollection: PageCollection, callback: PageIteratorCallback, requestOptions?: GraphRequestOptions) { this.client = client; this.collection = pageCollection.value; this.nextLink = pageCollection["@odata.nextLink"]; this.deltaLink = pageCollection["@odata.deltaLink"]; this.callback = callback; this.complete = false; + this.requestOptions = requestOptions; } /** @@ -116,7 +139,20 @@ export class PageIterator { */ private async fetchAndUpdateNextPageData(): Promise { try { - const response: PageCollection = await this.client.api(this.nextLink).get(); + let graphRequest = this.client.api(this.nextLink); + if (this.requestOptions) { + if (this.requestOptions.headers) { + graphRequest = graphRequest.headers(this.requestOptions.headers); + } + if (this.requestOptions.middlewareOptions) { + graphRequest = graphRequest.middlewareOptions(this.requestOptions.middlewareOptions); + } + if (this.requestOptions.options) { + graphRequest = graphRequest.options(this.requestOptions.options); + } + } + + const response: PageCollection = await graphRequest.get(); this.collection = response.value; this.nextLink = response["@odata.nextLink"]; this.deltaLink = response["@odata.deltaLink"]; From bdaf597396c62414fcea6b8f198f08b593d4123c Mon Sep 17 00:00:00 2001 From: nikithauc Date: Thu, 1 Oct 2020 16:14:52 -0700 Subject: [PATCH 42/55] Sorting parameter list - chaoshandleroptions (#334) --- docs/ChaosHandlerSamples.md | 2 +- spec/development/workload/PageIterator.ts | 2 +- spec/middleware/ChaosHandler.ts | 12 ++++++------ src/middleware/options/ChaosHandlerOptions.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/ChaosHandlerSamples.md b/docs/ChaosHandlerSamples.md index f0e3887b3..d87544c26 100644 --- a/docs/ChaosHandlerSamples.md +++ b/docs/ChaosHandlerSamples.md @@ -66,7 +66,7 @@ client // OverRiding to Random mode, providing the chaos percentage as 60(percentage times the error would be generated from handler) client .api("/me") - .middlewareOptions([new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.RANDOM, undefined, "I generated the error", 60)]) + .middlewareOptions([new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.RANDOM, "I generated the error", undefined, 60)]) .get() .then((res) => { console.log(res); diff --git a/spec/development/workload/PageIterator.ts b/spec/development/workload/PageIterator.ts index 12e9063e1..69a793bf9 100644 --- a/spec/development/workload/PageIterator.ts +++ b/spec/development/workload/PageIterator.ts @@ -113,7 +113,7 @@ describe("PageIterator", function() { return true; }; - const middlewareOptions = [new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200, "middleware options for pageIterator", 0, responseBody)]; + const middlewareOptions = [new ChaosHandlerOptions(ChaosStrategy.MANUAL, "middleware options for pageIterator", 200, 0, responseBody)]; const requestOptions = { middlewareOptions }; const client = Client.initWithMiddleware(clientOptions); diff --git a/spec/middleware/ChaosHandler.ts b/spec/middleware/ChaosHandler.ts index 8e6f605aa..782ecf58e 100644 --- a/spec/middleware/ChaosHandler.ts +++ b/spec/middleware/ChaosHandler.ts @@ -58,7 +58,7 @@ describe("ChaosHandler.ts", () => { }; it("Should return a valid response object for MANUAL case", () => { - chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404), cxt); + chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Manual response", 404), cxt); assert.isDefined(cxt.response); }); @@ -90,7 +90,7 @@ describe("ChaosHandler.ts", () => { }); it("Should send the request to the graph", async () => { - handler["sendRequest"](new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100), cxt); + handler["sendRequest"](new ChaosHandlerOptions(ChaosStrategy.RANDOM, "I generated the error", undefined, 100), cxt); assert.isDefined(cxt.response); }); }); @@ -143,13 +143,13 @@ describe("ChaosHandler.ts", () => { const tempChaosHandlerManualRegex = new ChaosHandler(tempManualOptionsRegex, manualMap); it("Should set a statusCode for MANUAL mode", () => { - const tempOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404); + const tempOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Set status code", 404); chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.GET); assert.isDefined(tempOptions.statusCode); }); it("Should set a statusCode for RANDOM mode", () => { - const tempOptions = new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100); + const tempOptions = new ChaosHandlerOptions(ChaosStrategy.RANDOM, "I generated the error", undefined, 100); chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.POST); assert.isDefined(tempOptions.statusCode); }); @@ -167,7 +167,7 @@ describe("ChaosHandler.ts", () => { describe("getOptions", () => { it("Should return the options in the context object", () => { - const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 405); + const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Get options", 405); const cxt: Context = { request: "url", middlewareControl: new MiddlewareControl([options]), @@ -240,7 +240,7 @@ describe("ChaosHandler.ts", () => { }); it("Should return response for Manual Request Level case", async () => { - const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200); + const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Manual Request level case", 200); const cxt: Context = { request: "https://graph.microsoft.com/v1.0/me", options: { diff --git a/src/middleware/options/ChaosHandlerOptions.ts b/src/middleware/options/ChaosHandlerOptions.ts index 1f3c0f6b1..836a1550d 100644 --- a/src/middleware/options/ChaosHandlerOptions.ts +++ b/src/middleware/options/ChaosHandlerOptions.ts @@ -60,13 +60,13 @@ export class ChaosHandlerOptions implements MiddlewareOptions { * @constructor * To create an instance of Testing Handler Options * @param {ChaosStrategy} ChaosStrategy - Specifies the startegy used for the Testing Handler -> RAMDOM/MANUAL - * @param {number?} statusCode - The statusCode to be returned in the response * @param {string} statusMessage - The Message to be returned in the response + * @param {number?} statusCode - The statusCode to be returned in the response * @param {number?} chaosPercentage - The percentage of randomness/chaos in the handler * @param {any?} responseBody - The response body to be returned in the response * @returns An instance of ChaosHandlerOptions */ - public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusCode?: number, statusMessage: string = "Some error Happened", chaosPercentage?: number, responseBody?: any) { + public constructor(chaosStrategy: ChaosStrategy = ChaosStrategy.RANDOM, statusMessage: string = "Some error Happened", statusCode?: number, chaosPercentage?: number, responseBody?: any) { this.chaosStrategy = chaosStrategy; this.statusCode = statusCode; this.statusMessage = statusMessage; From f99d0ff88f6ea36c019538625683c913da88368e Mon Sep 17 00:00:00 2001 From: nikithauc Date: Mon, 5 Oct 2020 15:26:51 -0700 Subject: [PATCH 43/55] 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 * merging with dev * Using spread operator * Checking if middleware is not empty Co-authored-by: Vincent Biret --- spec/core/Client.ts | 40 +++++++++++++++ spec/core/HTTPClient.ts | 31 ++++++++++++ spec/middleware/MiddlewareFactory.ts | 27 +++++++++++ src/Client.ts | 2 +- src/HTTPClient.ts | 42 ++++++++++++++-- src/HTTPClientFactory.ts | 9 ++-- src/IClientOptions.ts | 4 +- src/browser/index.ts | 6 ++- src/index.ts | 6 ++- src/middleware/MiddlewareFactory.ts | 62 ++++++++++++++++++++++++ src/tasks/OneDriveLargeFileUploadTask.ts | 5 +- 11 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 spec/middleware/MiddlewareFactory.ts create mode 100644 src/middleware/MiddlewareFactory.ts diff --git a/spec/core/Client.ts b/spec/core/Client.ts index 64165e8e7..55d98cc9e 100644 --- a/spec/core/Client.ts +++ b/spec/core/Client.ts @@ -8,10 +8,15 @@ import { assert } from "chai"; import "isomorphic-fetch"; +import { CustomAuthenticationProvider, TelemetryHandler } from "../../src"; import { Client } from "../../src/Client"; import { AuthProvider } from "../../src/IAuthProvider"; import { ClientOptions } from "../../src/IClientOptions"; import { Options } from "../../src/IOptions"; +import { AuthenticationHandler } from "../../src/middleware/AuthenticationHandler"; +import { ChaosHandler } from "../../src/middleware/ChaosHandler"; +import { ChaosHandlerOptions } from "../../src/middleware/options/ChaosHandlerOptions"; +import { ChaosStrategy } from "../../src/middleware/options/ChaosStrategy"; import { DummyAuthenticationProvider } from "../DummyAuthenticationProvider"; import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler"; @@ -61,6 +66,41 @@ describe("Client.ts", () => { assert.equal(error.name, "InvalidMiddlewareChain"); } }); + + it("Init middleware using a middleware array", async () => { + const provider: AuthProvider = (done) => { + done(null, "dummy_token"); + }; + const authHandler = new AuthenticationHandler(new CustomAuthenticationProvider(provider)); + const responseBody = "Test response body"; + const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Testing middleware array", 200, 0, responseBody); + const middlewareArray = [authHandler, new ChaosHandler(options)]; + const client = Client.initWithMiddleware({ middleware: middlewareArray }); + + const response = await client.api("me").get(); + assert.equal(response, responseBody); + }); + + it("Init middleware using a chained middleware array", async () => { + const provider: AuthProvider = (done) => { + done(null, "dummy_token"); + }; + const authHandler = new AuthenticationHandler(new CustomAuthenticationProvider(provider)); + + const responseBody = "Test response body"; + const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, "Testing chained middleware array", 200, 0, responseBody); + const chaosHandler = new ChaosHandler(options); + const telemetryHandler = new TelemetryHandler(); + + authHandler.setNext(telemetryHandler); + telemetryHandler.setNext(chaosHandler); + + const middlewareArray = [authHandler]; + const client = Client.initWithMiddleware({ middleware: middlewareArray }); + + const response = await client.api("me").get(); + assert.equal(response, responseBody); + }); }); describe("init", () => { diff --git a/spec/core/HTTPClient.ts b/spec/core/HTTPClient.ts index 8c00d9aee..33d6933f7 100644 --- a/spec/core/HTTPClient.ts +++ b/spec/core/HTTPClient.ts @@ -21,7 +21,38 @@ describe("HTTPClient.ts", () => { assert.isDefined(httpClient["middleware"]); assert.equal(httpClient["middleware"], httpMessageHandler); }); + + it("Should create an instance and populate middleware member when passing a middleware array", () => { + const client = new HTTPClient(...[httpMessageHandler]); + assert.isDefined(client["middleware"]); + assert.equal(client["middleware"], httpMessageHandler); + }); + + it("Should throw an error if middleware is undefined", () => { + try { + const client = new HTTPClient(); + } catch (error) { + assert.equal(error.name, "InvalidMiddlewareChain"); + } + }); + + it("Should throw an error if middleware is passed as null", () => { + try { + const client = new HTTPClient(null); + } catch (error) { + assert.equal(error.name, "InvalidMiddlewareChain"); + } + }); + + it("Should throw an error if middleware is passed as an empty array", () => { + try { + const client = new HTTPClient(...[]); + } catch (error) { + assert.equal(error.name, "InvalidMiddlewareChain"); + } + }); }); + /* tslint:enable: no-string-literal */ describe("sendRequest", async () => { diff --git a/spec/middleware/MiddlewareFactory.ts b/spec/middleware/MiddlewareFactory.ts new file mode 100644 index 000000000..33d71dcda --- /dev/null +++ b/spec/middleware/MiddlewareFactory.ts @@ -0,0 +1,27 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +import { assert } from "chai"; + +import { AuthenticationHandler, CustomAuthenticationProvider, HTTPMessageHandler, RedirectHandler, RetryHandler, TelemetryHandler } from "../../src"; +import { AuthProvider } from "../../src/IAuthProvider"; +import { MiddlewareFactory } from "../../src/middleware/MiddlewareFactory"; + +describe("MiddlewareFactory", () => { + it("Should return the default pipeline", () => { + const provider: AuthProvider = (done) => { + done(null, "dummy_token"); + }; + const defaultMiddleWareArray = MiddlewareFactory.getDefaultMiddlewareChain(new CustomAuthenticationProvider(provider)); + + assert.isTrue(defaultMiddleWareArray[0] instanceof AuthenticationHandler); + assert.isTrue(defaultMiddleWareArray[1] instanceof RetryHandler); + assert.isTrue(defaultMiddleWareArray[2] instanceof RedirectHandler); + assert.isTrue(defaultMiddleWareArray[3] instanceof TelemetryHandler); + assert.isTrue(defaultMiddleWareArray[4] instanceof HTTPMessageHandler); + }); +}); diff --git a/src/Client.ts b/src/Client.ts index 0cd9b04c8..ede9074a4 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -93,7 +93,7 @@ export class Client { } else if (clientOptions.authProvider !== undefined) { httpClient = HTTPClientFactory.createWithAuthenticationProvider(clientOptions.authProvider); } else if (clientOptions.middleware !== undefined) { - httpClient = new HTTPClient(clientOptions.middleware); + httpClient = new HTTPClient(...[].concat(clientOptions.middleware)); } else { const error = new Error(); error.name = "InvalidMiddlewareChain"; diff --git a/src/HTTPClient.ts b/src/HTTPClient.ts index 2276fb227..b61dd06ef 100644 --- a/src/HTTPClient.ts +++ b/src/HTTPClient.ts @@ -27,10 +27,46 @@ export class HTTPClient { * @public * @constructor * Creates an instance of a HTTPClient - * @param {Middleware} middleware - The first middleware of the middleware chain + * @param {...Middleware} middleware - The first middleware of the middleware chain or a sequence of all the Middleware handlers */ - public constructor(middleware: Middleware) { - this.middleware = middleware; + public constructor(...middleware: Middleware[]) { + if (!middleware || !middleware.length) { + const error = new Error(); + error.name = "InvalidMiddlewareChain"; + error.message = "Please provide a default middleware chain or custom middleware chain"; + throw error; + } + this.setMiddleware(...middleware); + } + + /** + * @private + * Processes the middleware parameter passed to set this.middleware property + * @param {...Middleware} middleware - The middleware passed + * @returns Nothing + */ + private setMiddleware(...middleware: Middleware[]): void { + if (middleware.length > 1) { + this.parseMiddleWareArray(middleware); + } else { + this.middleware = middleware[0]; + } + } + + /** + * @private + * Processes the middleware array to construct the chain + * and sets this.middleware property to the first middlware handler of the array + * @param {Middleware[]} middlewareArray - The array of middleware handlers + * @returns Nothing + */ + private parseMiddleWareArray(middlewareArray: Middleware[]) { + middlewareArray.forEach((element, index) => { + if (index < middlewareArray.length - 1) { + element.setNext(middlewareArray[index + 1]); + } + }); + this.middleware = middlewareArray[0]; } /** diff --git a/src/HTTPClientFactory.ts b/src/HTTPClientFactory.ts index 673b90dbe..d97fce35f 100644 --- a/src/HTTPClientFactory.ts +++ b/src/HTTPClientFactory.ts @@ -26,7 +26,7 @@ import { TelemetryHandler } from "./middleware/TelemetryHandler"; * @returns A boolean representing the environment is node or not */ const isNodeEnvironment = (): boolean => { - return new Function("try {return this === global;}catch(e){return false;}")(); // tslint:disable-line: function-constructor + return typeof process === "object" && typeof require === "function"; }; /** @@ -69,10 +69,11 @@ export class HTTPClientFactory { * @public * @static * Creates a middleware chain with the given one - * @param {Middleware} middleware - The first middleware of the middleware chain + * @property {...Middleware} middleware - The first middleware of the middleware chain or a sequence of all the Middleware handlers * @returns A HTTPClient instance */ - public static createWithMiddleware(middleware: Middleware): HTTPClient { - return new HTTPClient(middleware); + public static createWithMiddleware(...middleware: Middleware[]): HTTPClient { + // Middleware should not empty or undefined. This is check is present in the HTTPClient constructor. + return new HTTPClient(...middleware); } } diff --git a/src/IClientOptions.ts b/src/IClientOptions.ts index f87c10454..c3b0c2ea2 100644 --- a/src/IClientOptions.ts +++ b/src/IClientOptions.ts @@ -17,7 +17,7 @@ import { Middleware } from "./middleware/IMiddleware"; * @property {boolean} [debugLogging] - The boolean to enable/disable debug logging * @property {string} [defaultVersion] - The default version that needs to be used while making graph api request * @property {FetchOptions} [fetchOptions] - The options for fetch request - * @property {Middleware} [middleware] - The first middleware of the middleware chain + * @property {Middleware| Middleware[]} [middleware] - The first middleware of the middleware chain or an array of the Middleware handlers */ export interface ClientOptions { authProvider?: AuthenticationProvider; @@ -25,5 +25,5 @@ export interface ClientOptions { debugLogging?: boolean; defaultVersion?: string; fetchOptions?: FetchOptions; - middleware?: Middleware; + middleware?: Middleware | Middleware[]; } diff --git a/src/browser/index.ts b/src/browser/index.ts index dd5a0bec3..e6f54ae11 100644 --- a/src/browser/index.ts +++ b/src/browser/index.ts @@ -13,17 +13,21 @@ export * from "../middleware/HTTPMessageHandler"; export * from "../middleware/IMiddleware"; export * from "../middleware/RetryHandler"; export * from "../middleware/TelemetryHandler"; - +export * from "../middleware/MiddlewareFactory"; export * from "../middleware/options/AuthenticationHandlerOptions"; export * from "../middleware/options/IMiddlewareOptions"; export * from "../middleware/options/RetryHandlerOptions"; export * from "../middleware/options/TelemetryHandlerOptions"; +export * from "../middleware/options/ChaosHandlerOptions"; +export * from "../middleware/options/ChaosStrategy"; +export * from "../middleware/ChaosHandler"; export * from "../tasks/LargeFileUploadTask"; export * from "../tasks/OneDriveLargeFileUploadTask"; export * from "../tasks/PageIterator"; export * from "../Client"; +export * from "../CustomAuthenticationProvider"; export * from "../GraphError"; export * from "../GraphRequest"; export * from "../IAuthProvider"; diff --git a/src/index.ts b/src/index.ts index 6f0ccb39b..d0250f0e3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,18 +14,22 @@ export * from "./middleware/IMiddleware"; export * from "./middleware/RetryHandler"; export * from "./middleware/RedirectHandler"; export * from "./middleware/TelemetryHandler"; - +export * from "./middleware/MiddlewareFactory"; export * from "./middleware/options/AuthenticationHandlerOptions"; export * from "./middleware/options/IMiddlewareOptions"; export * from "./middleware/options/RetryHandlerOptions"; export * from "./middleware/options/RedirectHandlerOptions"; export * from "./middleware/options/TelemetryHandlerOptions"; +export * from "./middleware/options/ChaosHandlerOptions"; +export * from "./middleware/options/ChaosStrategy"; +export * from "./middleware/ChaosHandler"; export * from "./tasks/LargeFileUploadTask"; export * from "./tasks/OneDriveLargeFileUploadTask"; export * from "./tasks/PageIterator"; export * from "./Client"; +export * from "./CustomAuthenticationProvider"; export * from "./GraphError"; export * from "./GraphRequest"; export * from "./IAuthProvider"; diff --git a/src/middleware/MiddlewareFactory.ts b/src/middleware/MiddlewareFactory.ts new file mode 100644 index 000000000..619e55d63 --- /dev/null +++ b/src/middleware/MiddlewareFactory.ts @@ -0,0 +1,62 @@ +/** + * ------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. + * See License in the project root for license information. + * ------------------------------------------------------------------------------------------- + */ + +/** + * @module MiddlewareFactory + */ + +import { AuthenticationProvider } from "../IAuthenticationProvider"; + +import { AuthenticationHandler } from "./AuthenticationHandler"; +import { HTTPMessageHandler } from "./HTTPMessageHandler"; +import { Middleware } from "./IMiddleware"; +import { RedirectHandlerOptions } from "./options/RedirectHandlerOptions"; +import { RetryHandlerOptions } from "./options/RetryHandlerOptions"; +import { RedirectHandler } from "./RedirectHandler"; +import { RetryHandler } from "./RetryHandler"; +import { TelemetryHandler } from "./TelemetryHandler"; + +/** + * @private + * To check whether the environment is node or not + * @returns A boolean representing the environment is node or not + */ +const isNodeEnvironment = (): boolean => { + return typeof process === "object" && typeof require === "function"; +}; + +/** + * @class + * Class containing function(s) related to the middleware pipelines. + */ +export class MiddlewareFactory { + /** + * @public + * @static + * Returns the default middleware chain an array with the middleware handlers + * @param {AuthenticationProvider} authProvider - The authentication provider instance + * @returns an array of the middleware handlers of the default middleware chain + */ + public static getDefaultMiddlewareChain(authProvider: AuthenticationProvider): Middleware[] { + const middleware: Middleware[] = []; + const authenticationHandler = new AuthenticationHandler(authProvider); + const retryHandler = new RetryHandler(new RetryHandlerOptions()); + const telemetryHandler = new TelemetryHandler(); + const httpMessageHandler = new HTTPMessageHandler(); + + middleware.push(authenticationHandler); + middleware.push(retryHandler); + if (isNodeEnvironment()) { + const redirectHandler = new RedirectHandler(new RedirectHandlerOptions()); + middleware.push(redirectHandler); + } + middleware.push(telemetryHandler); + middleware.push(httpMessageHandler); + + return middleware; + } +} diff --git a/src/tasks/OneDriveLargeFileUploadTask.ts b/src/tasks/OneDriveLargeFileUploadTask.ts index 7f069ee28..44d52027c 100644 --- a/src/tasks/OneDriveLargeFileUploadTask.ts +++ b/src/tasks/OneDriveLargeFileUploadTask.ts @@ -61,7 +61,10 @@ export class OneDriveLargeFileUploadTask extends LargeFileUploadTask { } // we choose to encode each component of the file path separately because when encoding full URI // with encodeURI, special characters like # or % in the file name doesn't get encoded as desired - return `/me/drive/root:${path.split('/').map(p => encodeURIComponent(p)).join('/')}${encodeURIComponent(fileName)}:/createUploadSession`; + return `/me/drive/root:${path + .split("/") + .map((p) => encodeURIComponent(p)) + .join("/")}${encodeURIComponent(fileName)}:/createUploadSession`; } /** From ad4399bc85c47950fc481dd06c7909ed68fe2c60 Mon Sep 17 00:00:00 2001 From: Olivier Cuypers Date: Tue, 6 Oct 2020 00:51:02 +0200 Subject: [PATCH 44/55] Make GraphError real Error (#335) Co-authored-by: nikithauc --- spec/core/GraphErrorHandler.ts | 2 +- src/GraphError.ts | 13 ++++--------- src/GraphErrorHandler.ts | 6 ++---- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/spec/core/GraphErrorHandler.ts b/spec/core/GraphErrorHandler.ts index f16ae3ebf..e16c05a4a 100644 --- a/spec/core/GraphErrorHandler.ts +++ b/spec/core/GraphErrorHandler.ts @@ -97,9 +97,9 @@ describe("GraphErrorHandler.ts", () => { it("Should construct some default error", async () => { const gError = await GraphErrorHandler.getError(); + assert.equal(gError.message, ""); assert.equal(gError.statusCode, -1); assert.equal(gError.code, null); - assert.equal(gError.message, null); assert.equal(gError.body, null); assert.equal(gError.requestId, null); }); diff --git a/src/GraphError.ts b/src/GraphError.ts index 08dc7bde2..d3433e879 100644 --- a/src/GraphError.ts +++ b/src/GraphError.ts @@ -17,7 +17,7 @@ * Some fields are renamed ie, "request-id" => requestId so you can use dot notation */ -export class GraphError { +export class GraphError extends Error { /** * @public * A member holding status code of the error @@ -30,12 +30,6 @@ export class GraphError { */ public code: string | null; - /** - * @public - * A member holding error message - */ - public message: string | null; - /** * @public * A member holding request-id i.e identifier of the request @@ -61,12 +55,13 @@ export class GraphError { * @param {number} [statusCode = -1] - The status code of the error * @returns An instance of GraphError */ - public constructor(statusCode: number = -1) { + public constructor(statusCode: number = -1, message?: string, baseError?: Error) { + super(message || (baseError && baseError.message)); this.statusCode = statusCode; this.code = null; - this.message = null; this.requestId = null; this.date = new Date(); this.body = null; + this.stack = baseError ? baseError.stack : this.stack; } } diff --git a/src/GraphErrorHandler.ts b/src/GraphErrorHandler.ts index e5506663d..310596968 100644 --- a/src/GraphErrorHandler.ts +++ b/src/GraphErrorHandler.ts @@ -27,12 +27,11 @@ export class GraphErrorHandler { * @returns The GraphError instance */ private static constructError(error: Error, statusCode?: number): GraphError { - const gError = new GraphError(statusCode); + const gError = new GraphError(statusCode, "", error); if (error.name !== undefined) { gError.code = error.name; } gError.body = error.toString(); - gError.message = error.message; gError.date = new Date(); return gError; } @@ -60,9 +59,8 @@ export class GraphErrorHandler { */ private static constructErrorFromResponse(error: any, statusCode: number): GraphError { error = error.error; - const gError = new GraphError(statusCode); + const gError = new GraphError(statusCode, error.message); gError.code = error.code; - gError.message = error.message; if (error.innerError !== undefined) { gError.requestId = error.innerError["request-id"]; gError.date = new Date(error.innerError.date); From a95a762b9b7f66fb843aa1e134014f4214bc093a Mon Sep 17 00:00:00 2001 From: nikithauc Date: Tue, 6 Oct 2020 02:37:06 -0700 Subject: [PATCH 45/55] Using ternary shorthand, upgrade preview 2.1.0-2 (#337) --- package-lock.json | 2 +- package.json | 2 +- src/GraphRequest.ts | 2 +- src/GraphRequestUtil.ts | 2 +- src/Version.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08478c7a0..1d4e5120d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.1.0-Preview.1", + "version": "2.1.0-Preview.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 033467a57..752a6da6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.1.0-Preview.1", + "version": "2.1.0-Preview.2", "description": "Microsoft Graph Client Library", "license": "MIT", "main": "lib/src/index.js", diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 237266359..2077e5652 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -651,7 +651,7 @@ export class GraphRequest { method: RequestMethod.POST, body: serializeContent(content), }; - const className: string = content === undefined || content === null ? undefined : content.constructor.name; + const className: string = content && content.constructor && content.constructor.name; if (className === "FormData") { // Content-Type headers should not be specified in case the of FormData type content options.headers = {}; diff --git a/src/GraphRequestUtil.ts b/src/GraphRequestUtil.ts index 3d645ce6d..a9ab02bb1 100644 --- a/src/GraphRequestUtil.ts +++ b/src/GraphRequestUtil.ts @@ -41,7 +41,7 @@ export const urlJoin = (urlSegments: string[]): string => { */ export const serializeContent = (content: any): any => { - const className: string = content === undefined || content === null ? undefined : content.constructor.name; + const className: string = content && content.constructor && content.constructor.name; if (className === "Buffer" || className === "Blob" || className === "File" || className === "FormData" || typeof content === "string") { return content; } diff --git a/src/Version.ts b/src/Version.ts index bb4ac523c..64101ee23 100644 --- a/src/Version.ts +++ b/src/Version.ts @@ -12,4 +12,4 @@ * @module Version */ -export const PACKAGE_VERSION = "2.1.0-Preview.1"; +export const PACKAGE_VERSION = "2.1.0-Preview.2"; From 343bb2febef9c2b3bbabf64c38dfd9adb672e327 Mon Sep 17 00:00:00 2001 From: OfficeGlobal <47977325+OfficeGlobal@users.noreply.github.com> Date: Mon, 12 Oct 2020 11:21:01 +0200 Subject: [PATCH 46/55] HB of localized readme files (#268) Co-authored-by: OfficeGlobal Co-authored-by: Vincent Biret Co-authored-by: nikithauc --- README-Localized/README-es-es.md | 229 +++++++++++++++++++++++++++++++ README-Localized/README-fr-fr.md | 229 +++++++++++++++++++++++++++++++ README-Localized/README-ja-jp.md | 229 +++++++++++++++++++++++++++++++ README-Localized/README-pt-br.md | 229 +++++++++++++++++++++++++++++++ README-Localized/README-ru-ru.md | 229 +++++++++++++++++++++++++++++++ README-Localized/README-zh-cn.md | 229 +++++++++++++++++++++++++++++++ 6 files changed, 1374 insertions(+) create mode 100644 README-Localized/README-es-es.md create mode 100644 README-Localized/README-fr-fr.md create mode 100644 README-Localized/README-ja-jp.md create mode 100644 README-Localized/README-pt-br.md create mode 100644 README-Localized/README-ru-ru.md create mode 100644 README-Localized/README-zh-cn.md diff --git a/README-Localized/README-es-es.md b/README-Localized/README-es-es.md new file mode 100644 index 000000000..de872586d --- /dev/null +++ b/README-Localized/README-es-es.md @@ -0,0 +1,229 @@ +# Biblioteca cliente de JavaScript de Microsoft Graph + +[![npm version badge](https://img.shields.io/npm/v/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) [![Travis](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript.svg?maxAge=86400)](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript) [![Known Vulnerabilities](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript/badge.svg?maxAge=86400)](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript) [![Licence](https://img.shields.io/github/license/microsoftgraph/msgraph-sdk-javascript.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![Downloads](https://img.shields.io/npm/dm/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) + +La biblioteca cliente de JavaScript de Microsoft Graph es un envase reducido en torno a la API de Microsoft Graph que puede usarse en el lado del servidor y en el explorador. + +**¿Busca IntelliSense en los modelos (usuarios, grupos, etc.)? Vea el repositorio de [tipos de Microsoft Graph](https://github.com/microsoftgraph/msgraph-typescript-typings).** + +[![Demostración de TypeScript](https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-javascript/master/types-demo.PNG)](https://github.com/microsoftgraph/msgraph-typescript-typings) + +## Instalación + +### Mediante npm + +```cmd +npm install @microsoft/microsoft-graph-client +``` + +importe `@microsoft/microsoft-graph-client` en el módulo y también necesitará polyfills para capturar como [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch). + +```typescript +import "isomorphic-fetch"; +import { Client } from "@microsoft/microsoft-graph-client"; +``` + +### Mediante etiqueta de script + +Incluya [graph-js-sdk.js](https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js) en la página HTML. + +```HTML + +``` + +En caso de que el explorador no sea compatible con [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) \[[support](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility)] o [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) \[[support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility)], deberá usar polyfills como [github/fetch](https://github.com/github/fetch) para fetch y [es6-promise](https://github.com/stefanpenner/es6-promise) para promise. + +```HTML + + + + + + + + +``` + +## Introducción + +### 1. Registrar su aplicación + +Registre su aplicación para usar la API de Microsoft Graph con uno de los siguientes portales de autenticación compatibles: + +- [Portal de registro de aplicaciones de Microsoft](https://apps.dev.microsoft.com): Registre una nueva aplicación que funcione con cuentas de Microsoft y/o cuentas de la organización con el punto de conexión de autenticación V2 unificado. +- [Microsoft Azure Active Directory](https://manage.windowsazure.com): Registre una nueva aplicación en el espacio empresarial de Active Directory para dar soporte a los usuarios de cuentas profesionales o educativas de su espacio empresarial, o a varios espacios empresariales. + +### 2. Autenticarse para el servicio de Microsoft Graph + +La biblioteca cliente de JavaScript de Microsoft Graph tiene una implementación de adaptador ([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts)) para [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core) (biblioteca de autenticación de Microsoft) que se ocupa de recibir el `accessToken`. La biblioteca MSAL no se incluye con esta biblioteca, el usuario debe incluirla de forma externa (para incluir MSAL, consulte [esto](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)). + +> **Nota importante:** MSAL solo es compatible con las aplicaciones front-end, para la autenticación en el servidor tiene que implementar su propio AuthenticationProvider. Obtenga información acerca de cómo puede crear un [proveedor de autenticación personalizado](./docs/CustomAuthenticationProvider.md). + +#### Creación de una instancia de ImplicitMSALAuthenticationProvider en un entorno de explorador + +Consulte devDependencies en [package.json](./package.json) para ver la versión compatible de msal y actualícela a continuación. + +```html + +``` + +```typescript + +// Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new Msal.UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +#### Creación de una instancia de ImplicitMSALAuthenticationProvider en un entorno de nodo + +Consulte devDependencies en [package.json](./package.json) para ver la versión compatible de msal y actualícela a continuación. + +```cmd +npm install msal@ +``` + +```typescript +import { UserAgentApplication } from "msal"; + +import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; + +// An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +El usuario puede integrar la biblioteca de autenticación preferida mediante la implementación de `IAuthenticationProvider` interfaz. Consulte la implementación de un [proveedor de autenticación personalizada](./docs/CustomAuthenticationProvider.md). + +### 3. Inicializar un objeto de cliente de Microsoft Graph con un proveedor de autenticación + +Una instancia de la clase **cliente** controla las solicitudes a la API de Microsoft Graph y procesa las respuestas. Para crear una nueva instancia de esta clase, tiene que proporcionar una instancia de [`IAuthenticationProvider`](src/IAuthenticationProvider.ts) que necesita pasarse como un valor para la clave `authProvider` en [`ClientOptions`](src/IClientOptions.ts) a un método de inicializador estático `Client.initWithMiddleware`. + +#### Para entornos de navegador + +```typescript +const options = { + authProvider, // An instance created from previous step +}; +const Client = MicrosoftGraph.Client; +const client = Client.initWithMiddleware(options); +``` + +#### Para entornos de nodo + +```typescript +import { Client } from "@microsoft/microsoft-graph-client"; + +const options = { + authProvider, // An instance created from previous step +}; +const client = Client.initWithMiddleware(options); +``` + +Para obtener más información sobre cómo inicializar el cliente, consulte [este documento](./docs/CreatingClientInstance.md). + +### 4. Realizar solicitudes a Graph + +Una vez que haya configurado la autenticación y una instancia del cliente, puede empezar a realizar llamadas al servicio. Todas las solicitudes deberían empezar con `client.api(path)` y terminar con una [acción](./docs/Actions.md). + +Obtención de detalles de usuario + +```typescript +try { + let userDetails = await client.api("/me").get(); + console.log(userDetails); +} catch (error) { + throw error; +} +``` + +Enviar un correo electrónico a los destinatarios + +```typescript +// Construct email object +const mail = { + subject: "Microsoft Graph JavaScript Sample", + toRecipients: [ + { + emailAddress: { + address: "example@example.com", + }, + }, + ], + body: { + content: "

MicrosoftGraph JavaScript Sample

Check out https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +try { + let response = await client.api("/me/sendMail").post({ message: mail }); + console.log(response); +} catch (error) { + throw error; +} +``` + +Para obtener más información, consulte: [Patrón de llamada](docs/CallingPattern.md), [acciones](docs/Actions.md), [parámetros de consulta](docs/QueryParameters.md), [métodos de la API](docs/OtherAPIs.md) y [más](docs/). + +## Documentación + +- [Procesamiento por lotes](docs/content/Batching.md) +- [Tarea de carga de archivos de gran tamaño](docs/tasks/LargeFileUploadTask.md) +- [Iterador de páginas](docs/tasks/PageIterator.md) +- [Acciones](docs/Actions.md) +- [Parámetros de consulta](docs/QueryParameters.md) +- [Otras API](docs/OtherAPIs.md) +- [Obtener respuesta sin procesar](docs/GettingRawResponse.md) + +## Preguntas y comentarios + +Nos encantaría recibir sus comentarios sobre la biblioteca cliente de JavaScript de Microsoft Graph. Puede enviarnos sus preguntas y sugerencias a través de la sección [Problemas](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues) de este repositorio. + +## Colaboradores + +Vea la [directrices de contribución](CONTRIBUTING.md). + +## Recursos adicionales + +- [Sitio web de Microsoft Graph](https://graph.microsoft.io) +- [Tipos de TypeScript de Microsoft Graph](https://github.com/microsoftgraph/msgraph-typescript-typings/) +- [Crear aplicaciones de página única en Angular con Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-angularspa) +- [Crear aplicaciones Node.js Express con Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-nodeexpressapp) +- [Centro para desarrolladores de Office](http://dev.office.com/) + +## Avisos de terceros + +Consulte [avisos de terceros](./THIRD%20PARTY%20NOTICES) para obtener información sobre los paquetes que se incluyen en [package.json](./package.json) + +## Informes de seguridad + +Si encuentra un problema de seguridad con nuestras bibliotecas o servicios, informe a [secure@microsoft.com](mailto:secure@microsoft.com) con todos los detalles posibles. Es posible que el envío pueda optar a una recompensa a través del programa [Microsoft Bounty](http://aka.ms/bugbounty). No publique problemas de seguridad en problemas de GitHub ni ningún otro sitio público. Nos pondremos en contacto con usted rápidamente tras recibir la información. Le animamos a que obtenga notificaciones de los incidentes de seguridad que se produzcan; para ello, visite [esta página](https://technet.microsoft.com/en-us/security/dd252948) y suscríbase a las alertas de avisos de seguridad. + +## Licencia + +Copyright (c) Microsoft Corporation. Todos los derechos reservados. Publicado bajo la licencia MIT (la "[Licencia](./LICENSE)"). + +## Valoramos y nos adherimos al Código de conducta de código abierto de Microsoft + +Este proyecto ha adoptado el [Código de conducta de código abierto de Microsoft](https://opensource.microsoft.com/codeofconduct/). Para obtener más información, vea [Preguntas frecuentes sobre el código de conducta](https://opensource.microsoft.com/codeofconduct/faq/) o póngase en contacto con [opencode@microsoft.com](mailto:opencode@microsoft.com) si tiene otras preguntas o comentarios. diff --git a/README-Localized/README-fr-fr.md b/README-Localized/README-fr-fr.md new file mode 100644 index 000000000..465e6a911 --- /dev/null +++ b/README-Localized/README-fr-fr.md @@ -0,0 +1,229 @@ +# Bibliothèque cliente JavaScript Microsoft Graph + +[![badge version npm](https://img.shields.io/npm/v/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) [![Travis](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript.svg?maxAge=86400)](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript) [![Vulnérabilités connues](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript/badge.svg?maxAge=86400)](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript) [![Licence](https://img.shields.io/github/license/microsoftgraph/msgraph-sdk-javascript.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![style de code : plus beau](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![Téléchargements](https://img.shields.io/npm/dm/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) + +La bibliothèque client JavaScript de Microsoft Graph est une enveloppe légère autour de l'API Microsoft Graph qui peut être utilisée côté serveur et dans le navigateur. + +**Vous recherchez IntelliSense sur les modèles (utilisateurs, groupes, etc.) ? Consultez les [types Microsoft Graph](https://github.com/microsoftgraph/msgraph-typescript-typings) référentiel !** + +[![TypeScript demo](https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-javascript/master/types-demo.PNG)](https://github.com/microsoftgraph/msgraph-typescript-typings) + +## Installation + +### Via NPM + +```cmd +npm install @microsoft/microsoft-graph-client +``` + +Importez `@microsoft/Microsoft-Graph-client` dans votre module. vous avez également besoin de polyremplissages pour la récupération tels que [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch). + +```typescript +import "isomorphic-fetch"; +import { Client } from "@microsoft/microsoft-graph-client"; +``` + +### Via la balise de script + +Incluez [graph-js-sdk.js](https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js) dans votre page HTML. + +```HTML + +``` + +Si votre navigateur ne prend pas en charge les fonctions [Récupérer](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) \[[support](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility)] ou [Promesse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) \[[support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility)], vous devez utiliser des polyremplissages tels que [github/fetch](https://github.com/github/fetch) pour la récupération et [ES6 promise](https://github.com/stefanpenner/es6-promise) pour les promesses. + +```HTML + + + + + + + + +``` + +## Prise en main + +### 1. Inscription de votre application + +Enregistrez votre application à l’aide de l’un des portails d’authentification pris en charge suivants pour utiliser l'API Microsoft Graph : + +- [portail d’inscription des applications de Microsoft](https://apps.dev.microsoft.com) : Enregistrez une nouvelle application qui fonctionne avec des comptes Microsoft et/ou des comptes d’organisation à l’aide du point de terminaison d’authentification V2 unifié. +- [Microsoft Azure Active Directory](https://manage.windowsazure.com) : Enregistrez une nouvelle application dans l’annuaire Active Directory de votre locataire pour prendre en charge les utilisateurs professionnels ou scolaires de votre locataire ou de plusieurs clients. + +### 2. S'authentifier au service de Microsoft Graph + +La bibliothèque client JavaScript Microsoft Graph inclut une implémentation de carte ([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts)) pour [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core) (bibliothèque d’authentification Microsoft) qui prend en charge l’acquisition de `accessToken`. La bibliothèque MSAL n’est pas livrée avec cette bibliothèque, l’utilisateur doit l’inclure en externe (pour inclure MSAL, consultez [cet](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)). + +> **Remarque importante :** MSAL est pris en charge uniquement pour les applications frontend, pour l’authentification côté serveur, vous devez implémenter votre propre AuthenticationProvider. Découvrez comment créer un [fournisseur d’authentification personnalisée](./docs/CustomAuthenticationProvider.md). + +#### Création d’une instance de ImplicitMSALAuthenticationProvider dans l’environnement de navigateur + +Reportez-vous à devDependencies dans [package.json](./package.json) pour la version MSAL compatible et mettez à jour cette version dans la version suivante. + +```html + +``` + +```typescript + +// Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new Msal.UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +#### Création d’une instance de ImplicitMSALAuthenticationProvider dans l’environnement de nœud + +Reportez-vous à devDependencies dans [package.json](./package.json) pour la version MSAL compatible et mettez à jour cette version dans la version suivante. + +```cmd +npm install msal@ +``` + +```typescript +import { UserAgentApplication } from "msal"; + +import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; + +// An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +Un utilisateur peut intégrer sa propre bibliothèque d’authentification par défaut en implémentant l’interface `IAuthenticationProvider`. Référez-vous à l’implémentation du [Fournisseur d’authentification personnalisé](./docs/CustomAuthenticationProvider.md). + +### 3. Initialiser un objet client Microsoft Graph avec un fournisseur d’authentification + +Une instance de la classe **Client** gère les demandes en les envoyant vers l’API Microsoft Graph et en traitant les réponses. Pour créer une instance de cette classe, vous devez fournir une instance de [`IAuthenticationProvider`](src/IAuthenticationProvider.ts) lequel doit être transmis sous la forme d’une valeur pour la clé `authProvider dans` [`ClientOptions`](src/IClientOptions.ts) à une méthode d'initialisation statique `Client.initWithMiddleware`. + +#### Pour l’environnement de navigateur + +```typescript +const options = { + authProvider, // An instance created from previous step +}; +const Client = MicrosoftGraph.Client; +const client = Client.initWithMiddleware(options); +``` + +#### Pour l'environnement du nœud + +```typescript +import { Client } from "@microsoft/microsoft-graph-client"; + +const options = { + authProvider, // An instance created from previous step +}; +const client = Client.initWithMiddleware(options); +``` + +Pour plus d'informations sur l'initialisation du client, consultez [ce document](./docs/CreatingClientInstance.md). + +### 4. Formuler des demandes auprès du Graph + +Une fois que vous disposez d’une configuration d’authentification et d’une instance de client, vous pouvez commencer à effectuer des appels vers le service. Toutes les demandes doivent commencer avec `client.api(path)` et se terminent par une [action](./docs/Actions.md). + +Obtenir les détails de l’utilisateur + +```typescript +try { + let userDetails = await client.api("/me").get(); + console.log(userDetails); +} catch (error) { + throw error; +} +``` + +Envoyez un courrier électronique aux destinataires + +```typescript +// Construct email object +const mail = { + subject: "Microsoft Graph JavaScript Sample", + toRecipients: [ + { + emailAddress: { + address: "example@example.com", + }, + }, + ], + body: { + content: "

MicrosoftGraph JavaScript Sample

Check out https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +try { + let response = await client.api("/me/sendMail").post({ message: mail }); + console.log(response); +} catch (error) { + throw error; +} +``` + +Pour plus d’informations, consultez : [Modèle d’appels](docs/CallingPattern.md), [Actions](docs/Actions.md), [Paramètres de requête](docs/QueryParameters.md)[Méthodes API](docs/OtherAPIs.md) et [plus](docs/). + +## Documentation + +- [Traitement par lots](docs/content/Batching.md) +- [Tâche de chargement d’un fichier volumineux](docs/tasks/LargeFileUploadTask.md) +- [Itérateur de page](docs/tasks/PageIterator.md) +- [Actions](docs/Actions.md) +- [ Paramètres de requête](docs/QueryParameters.md) +- [Autres API](docs/OtherAPIs.md) +- [Accès aux réponses brutes](docs/GettingRawResponse.md) + +## Questions et commentaires + +Nous serions ravis de connaître votre opinion sur la bibliothèque client JavaScript de Microsoft Graph. Vous pouvez nous faire part de vos questions et suggestions dans la rubrique [Problèmes](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues) de ce référentiel. + +## Contribution + +Reportez-vous aux [instructions sur la contribution](CONTRIBUTING.md). + +## Ressources supplémentaires + +- [Site web Microsoft Graph](https://graph.microsoft.io) +- [Types TypeScript Microsoft Graph](https://github.com/microsoftgraph/msgraph-typescript-typings/) +- [Créez des applications page simple Angular avec Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-angularspa) +- [Créez des applications Node.js Express avec Microsoft Graph ](https://github.com/microsoftgraph/msgraph-training-nodeexpressapp) +- [Centre des développeurs Office](http://dev.office.com/) + +## Notifications tierces + +Consultez les [Notifications tierces](./THIRD%20PARTY%20NOTICES) pour des informations sur les paquets qui sont inclus dans le [package.json](./package.json) + +## Génération de rapports de sécurité + +Si vous rencontrez un problème de sécurité avec nos bibliothèques ou services, signalez-le à l’adresse [secure@microsoft.com](mailto:secure@microsoft.com) avec autant de détails que possible. Votre envoi vous donnera sans doute droit à une prime via le programme [Bounty de Microsoft](http://aka.ms/bugbounty). Merci de ne pas publier de problèmes de sécurité sur le site des problèmes GitHub ou sur un autre site public. Nous vous contacterons rapidement dès réception des informations. Nous vous encourageons à activer les notifications d’incidents de sécurité en vous rendant sur [cette page](https://technet.microsoft.com/en-us/security/dd252948) et en vous abonnant aux alertes d’avis de sécurité. + +## Licence + +Copyright (c) Microsoft Corporation. Tous droits réservés. Soumis à la licence MIT (la [« Licence](./LICENSE) ») ; + +## Nous respectons le code de conduite Open Source de Microsoft. + +Ce projet a adopté le [code de conduite Open Source de Microsoft](https://opensource.microsoft.com/codeofconduct/). Pour en savoir plus, reportez-vous à la [FAQ relative au code de conduite](https://opensource.microsoft.com/codeofconduct/faq/) ou contactez [opencode@microsoft.com](mailto:opencode@microsoft.com) pour toute question ou tout commentaire. diff --git a/README-Localized/README-ja-jp.md b/README-Localized/README-ja-jp.md new file mode 100644 index 000000000..9c347ef83 --- /dev/null +++ b/README-Localized/README-ja-jp.md @@ -0,0 +1,229 @@ +# Microsoft Graph JavaScript クライアント ライブラリ + +[![npm バージョン バッジ](https://img.shields.io/npm/v/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) [![Travis](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript.svg?maxAge=86400)](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript) [![既知の脆弱性](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript/badge.svg?maxAge=86400)](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript) [![ライセンス](https://img.shields.io/github/license/microsoftgraph/msgraph-sdk-javascript.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![コード スタイル: より見栄え良く](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![ダウンロード](https://img.shields.io/npm/dm/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) + +Microsoft Graph JavaScript クライアント ライブラリは、Microsoft Graph API の軽量ラッパーであり、サーバー側およびブラウザー内で使用できます。 + +**モデル (ユーザー、グループなど) で IntelliSense を探していますか ?[Microsoft Graph Types](https://github.com/microsoftgraph/msgraph-typescript-typings) リポジトリをご覧ください !** + +[![TypeScript でも](https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-javascript/master/types-demo.PNG)](https://github.com/microsoftgraph/msgraph-typescript-typings) + +## インストール + +### npm 経由 + +```cmd +npm install @microsoft/microsoft-graph-client +``` + +モジュールに `@microsoft/microsoft-graph-client` をインポートします。また、[isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch) のようなフェッチのためのポリフィルが必要になります。 + +```typescript +import "isomorphic-fetch"; +import { Client } from "@microsoft/microsoft-graph-client"; +``` + +### スクリプト タグ経由 + +[graph-js-sdk.js](https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js) を HTML ページに含めます。 + +```HTML + +``` + +ブラウザが [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) \[[サポート](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility)] または [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) \[[サポート](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility)] をサポートしていない場合、フェッチには [github/fetch](https://github.com/github/fetch)、Promise には [es6-promise](https://github.com/stefanpenner/es6-promise) のようなポリフィルを使用する必要があります。 + +```HTML + + + + + + + + +``` + +## はじめに + +### 1.アプリケーションを登録する + +サポートされている次の認証ポータルのいずれかを使用して、Microsoft Graph API を使用するアプリケーションを登録します。 + +- [Microsoft アプリケーション登録ポータル](https://apps.dev.microsoft.com):統合 V2 認証エンドポイントを使用して、Microsoft アカウントや組織のアカウントで機能する新しいアプリケーションを登録します。 +- [Microsoft Azure Active Directory](https://manage.windowsazure.com)テナントまたは複数のテナントで職場または学校のユーザーをサポートするために、テナントの Active Directory に新しいアプリケーションを登録します。 + +### 2.Microsoft Graph サービスの認証 + +Microsoft Graph JavaScript クライアント ライブラリには、`accessToken` の取得を処理する [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core) (Microsoft Authentication Library) のアダプター実装 ([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts)) があります。MSAL ライブラリはこのライブラリに同梱されていません。ユーザーは外部でそれを含める必要があります (MSAL を含めるには、[こちら](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)を参照してください)。 + +> **重要なメモ:**MSAL は、フロントエンド アプリケーションでのみサポートされます。サーバー側の認証では、独自に AuthenticationProvider を実装する必要があります。[カスタム認証プロバイダー](./docs/CustomAuthenticationProvider.md)を作成する方法について説明します。 + +#### ブラウザー環境で ImplicitMSALAuthenticationProvider のインスタンスを作成する + +互換性のある MSAL バージョンについては、[package.json](./package.json) の devDependencies を参照し、以下でそのバージョンを更新してください。 + +```html + +``` + +```typescript + +// Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new Msal.UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +#### ノード環境で ImplicitMSALAuthenticationProvider のインスタンスを作成する + +互換性のある MSAL バージョンについては、[package.json](./package.json) の devDependencies を参照し、以下でそのバージョンを更新してください。 + +```cmd +npm install msal@ +``` + +```typescript +import { UserAgentApplication } from "msal"; + +import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; + +// An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +ユーザーは `IAuthenticationProvider` インターフェイスを実装することにより、自分好みの認証ライブラリを統合できます。[カスタム認証プロバイダー](./docs/CustomAuthenticationProvider.md)の実装を参照してください。 + +### 3.認証プロバイダーで Microsoft Graph クライアント オブジェクトを初期化する + +**Client** クラスのインスタンスは、Microsoft Graph API への要求に対処し、応答を処理します。このクラスの新しいインスタンスを作成するには、[`ClientOptions`](src/IClientOptions.ts) の `authProvider` キーの値として静的初期化メソッド `Client.initWithMiddleware` に渡す必要がある [`IAuthenticationProvider`](src/IAuthenticationProvider.ts) のインスタンスを提供する必要があります。 + +#### ブラウザー環境の場合 + +```typescript +const options = { + authProvider, // An instance created from previous step +}; +const Client = MicrosoftGraph.Client; +const client = Client.initWithMiddleware(options); +``` + +#### ノード環境の場合 + +```typescript +import { Client } from "@microsoft/microsoft-graph-client"; + +const options = { + authProvider, // An instance created from previous step +}; +const client = Client.initWithMiddleware(options); +``` + +クライアントの初期化の詳細については、[こちらのドキュメント](./docs/CreatingClientInstance.md)を参照してください。 + +### 4.Graph への要求を実行する + +認証のセットアップとクライアントのインスタンスを用意したら、サービスの呼び出しを開始できます。すべてのリクエストは `client.api(path)` で始まり、[action](./docs/Actions.md) で終わる必要があります。 + +ユーザーの詳細の取得 + +```typescript +try { + let userDetails = await client.api("/me").get(); + console.log(userDetails); +} catch (error) { + throw error; +} +``` + +受信者にメールを送信する + +```typescript +// Construct email object +const mail = { + subject: "Microsoft Graph JavaScript Sample", + toRecipients: [ + { + emailAddress: { + address: "example@example.com", + }, + }, + ], + body: { + content: "

MicrosoftGraph JavaScript Sample

Check out https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +try { + let response = await client.api("/me/sendMail").post({ message: mail }); + console.log(response); +} catch (error) { + throw error; +} +``` + +詳細については、以下を参照してください:[呼び出しパターン](docs/CallingPattern.md)、[アクション](docs/Actions.md)、[クエリ パラメーター](docs/QueryParameters.md)、[API メソッド](docs/OtherAPIs.md)および[その他](docs/)。 + +## ドキュメント + +- [バッチ処理](docs/content/Batching.md) +- [大容量ファイルのアップロード タスク](docs/tasks/LargeFileUploadTask.md) +- [ページの反復子](docs/tasks/PageIterator.md) +- [アクション](docs/Actions.md) +- [クエリ パラメーター](docs/QueryParameters.md) +- [その他の API](docs/OtherAPIs.md) +- [Raw 応答の取得](docs/GettingRawResponse.md) + +## 質問とコメント + +Microsoft Graph JavaScript クライアント ライブラリに関するフィードバックをお寄せください。質問や提案につきましては、このリポジトリの「[問題](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues)」セクションで送信できます。 + +## 投稿 + +[投稿ガイドライン](CONTRIBUTING.md)を参照してください。 + +## その他のリソース + +- [Microsoft Graph Web サイト](https://graph.microsoft.io) +- [Microsoft Graph TypeScript 型](https://github.com/microsoftgraph/msgraph-typescript-typings/) +- [Microsoft Graph を使った Angular の単一ページ アプリの作成](https://github.com/microsoftgraph/msgraph-training-angularspa) +- [Microsoft Graph を使った Node.js Express アプリの作成](https://github.com/microsoftgraph/msgraph-training-nodeexpressapp) +- [Office デベロッパー センター](http://dev.office.com/) + +## サードパーティについての通知 + +[package.json](./package.json) に含まれるパッケージの詳細については、[サードパーティについての通知](./THIRD%20PARTY%20NOTICES)を参照してください + +## セキュリティ レポート + +ライブラリまたはサービスでセキュリティに関する問題を発見した場合は、できるだけ詳細に [secure@microsoft.com](mailto:secure@microsoft.com) に報告してください。提出物は、[Microsoft Bounty](http://aka.ms/bugbounty) プログラムを通じて報酬を受ける対象となる場合があります。セキュリティの問題を GitHub の問題や他のパブリック サイトに投稿しないでください。情報を受け取り次第、ご連絡させていただきます。セキュリティの問題が発生したときに通知を受け取ることをお勧めします。そのためには、[このページ](https://technet.microsoft.com/en-us/security/dd252948)にアクセスし、セキュリティ アドバイザリ通知を受信登録してください。 + +## ライセンス + +Copyright (c) Microsoft Corporation。All rights reserved.MIT ライセンス ("[ライセンス](./LICENSE)") に基づいてライセンスされています。 + +## Microsoft Open Source Code of Conduct (Microsoft オープン ソース倫理規定) を尊重し、遵守します + +このプロジェクトでは、[Microsoft Open Source Code of Conduct (Microsoft オープン ソース倫理規定)](https://opensource.microsoft.com/codeofconduct/) が採用されています。詳細については、「[Code of Conduct FAQ (倫理規定の FAQ)](https://opensource.microsoft.com/codeofconduct/faq/)」を参照してください。また、その他の質問やコメントがあれば、[opencode@microsoft.com](mailto:opencode@microsoft.com) までお問い合わせください。 diff --git a/README-Localized/README-pt-br.md b/README-Localized/README-pt-br.md new file mode 100644 index 000000000..1ee408fba --- /dev/null +++ b/README-Localized/README-pt-br.md @@ -0,0 +1,229 @@ +# Biblioteca do cliente Microsoft Graph JavaScript + +emblema da versão npm Travis Vulnerabilidades conhecidas Licença estilo do código: prettier [![Downloads](https://img.shields.io/npm/dm/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) + +A biblioteca do cliente Microsoft Graph JavaScript é um invólucro leve em torno da API do Microsoft Graph, que pode ser usada no lado do servidor e no navegador. + +**Procurando o IntelliSense nos modelos (usuários, grupos, etc.)? Confira o](https://github.com/microsoftgraph/msgraph-typescript-typings) repositório [do Microsoft Graph Types!** + +[![Demonstração TypeScript ](https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-javascript/master/types-demo.PNG)](https://github.com/microsoftgraph/msgraph-typescript-typings) + +## Instalação + +### Via npm + +```cmd +npm install @microsoft/microsoft-graph-client +``` + +importe `@microsoft/microsoft-graph-client` para o seu módulo e, além disso, você precisará de metapreenchimentos de busca, como [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch). + +```typescript +import "isomorphic-fetch"; +import { Client } from "@microsoft/microsoft-graph-client"; +``` + +### Via marca de script + +Inclua [graph-js-sdk.js](https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js) na página HTML. + +```HTML + +``` + +Caso o seu navegador não tenha suporte para [Buscar](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) \[[suporte](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility)] ou [Prometer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) \[[suporte](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility)], você precisará usar o polipreenchimento, como [github/fetch](https://github.com/github/fetch) para a busca e [es6-promise](https://github.com/stefanpenner/es6-promise) para a promessa. + +```HTML + + + + + + + + +``` + +## Introdução + +### 1. Registre seu aplicativo + +Registre seu aplicativo para usar a API do Microsoft Graph usando um dos seguintes portais de autenticação com suporte: + +- Vá até o [](https://apps.dev.microsoft.com)Portal de Registro de Aplicativos da Microsoft: Registre um novo aplicativo que funcione com contas da Microsoft e/ou contas organizacionais usando o Ponto final de autenticação v2 unificado. +- [Microsoft Azure Active Directory](https://manage.windowsazure.com): Registre um novo aplicativo no Active Directory do locatário para oferecer suporte aos usuários corporativos ou estudantes para seu locatários ou vários locatários. + +### 2. Autentique o serviço do Microsoft Graph + +A biblioteca do cliente Microsoft Graph JavaScript possui uma implementação de adaptador ([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts)) para [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core) (Biblioteca de autenticação da Microsoft) que obtém o `accessToken`. A Biblioteca MSAL não é fornecida com essa biblioteca, o usuário deve incluí-la externamente (Para incluir MSAL, consulte [isso](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)). + +> **Observação importante:** O MSAL é suportado apenas por aplicativos frontend; para autenticação no servidor, você precisa implementar seu próprio AuthenticationProvider. Saiba como você pode criar um [Provedor de autenticação personalizado](./docs/CustomAuthenticationProvider.md). + +#### Crie uma instância do ImplicitMSALAuthenticationProvider no ambiente do navegador + +Consulte devDependencies no [package.json](./package.json) para a versão msal compatível e atualize essa versão no abaixo. + +```html + +``` + +```typescript + +// Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new Msal.UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +#### Crie uma instância do ImplicitMSALAuthenticationProvider no ambiente do navegador + +Consulte devDependencies no [package.json](./package.json) para a versão msal compatível e atualize essa versão no abaixo. + +```cmd +npm install msal@ +``` + +```typescript +import { UserAgentApplication } from "msal"; + +import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; + +// An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +O usuário pode integrar a própria biblioteca de autenticação preferencial implementando a interface do `IAuthenticationProvider`. Consulte implementar [Provedor de autenticação personalizada](./docs/CustomAuthenticationProvider.md). + +### 3. Inicialize um objeto Microsoft Graph Client com um provedor de autenticação + +Uma instância da classe **Client** lida com solicitações à API do Microsoft Graph e processa as respostas. Para criar uma nova instância dessa classe, você precisa fornecer uma instância de IAuthenticationProvider que deve ser passada como um valor para a chave authProvider em Clienteoptions para um método inicializador estático `Client.initWithMiddleware`. + +#### Para o ambiente do navegador + +```typescript +const options = { + authProvider, // An instance created from previous step +}; +const Client = MicrosoftGraph.Client; +const client = Client.initWithMiddleware(options); +``` + +#### Para o ambiente do nó + +```typescript +import { Client } from "@microsoft/microsoft-graph-client"; + +const options = { + authProvider, // An instance created from previous step +}; +const client = Client.initWithMiddleware(options); +``` + +Para saber mais sobre a inicialização do cliente, confira [este documento](./docs/CreatingClientInstance.md). + +### 4. Fazer solicitações ao gráfico + +Depois de configurar a autenticação e uma instância do Cliente, você poderá começar a fazer chamadas para o serviço. Todas as solicitações devem começar com `client.api(path)` e terminar com uma [ação](./docs/Actions.md). + +Obter detalhes do usuário + +```typescript +try { + let userDetails = await client.api("/me").get(); + console.log(userDetails); +} catch (error) { + throw error; +} +``` + +Enviar um e-mail aos destinatários + +```typescript +// Construct email object +const mail = { + subject: "Microsoft Graph JavaScript Sample", + toRecipients: [ + { + emailAddress: { + address: "example@example.com", + }, + }, + ], + body: { + content: "

MicrosoftGraph JavaScript Sample

Check out https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +try { + let response = await client.api("/me/sendMail").post({ message: mail }); + console.log(response); +} catch (error) { + throw error; +} +``` + +Para obter mais informações, consulte: [Padrão da chamada](docs/CallingPattern.md), [Ações](docs/Actions.md), [Parâmetros de consulta](docs/QueryParameters.md), [Métodos API](docs/OtherAPIs.md) e [mais](docs/). + +## Documentação + +- [Envio em lote](docs/content/Batching.md) +- [Tarefa de carregar arquivos grandes](docs/tasks/LargeFileUploadTask.md) +- [Iterador de página](docs/tasks/PageIterator.md) +- [Ações](docs/Actions.md) +- [ Parâmetros de consulta](docs/QueryParameters.md) +- [Outras APIs](docs/OtherAPIs.md) +- [Obter uma resposta pura](docs/GettingRawResponse.md) + +## Perguntas e comentários + +Adoraríamos receber seus comentários sobre o projeto Biblioteca do cliente Microsoft Graph JavaScript. Você pode enviar perguntas e sugestões na seção [Problemas](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues) deste repositório. + +## Colaboração + +Confira as [diretrizes de colaboração](CONTRIBUTING.md). + +## Recursos adicionais + +- [](https://graph.microsoft.io)Website do Microsoft Graph +- [Tipos de TypeScript do Microsoft Graph](https://github.com/microsoftgraph/msgraph-typescript-typings/) +- [Criar aplicativos angulares de página simples com o Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-angularspa) +- [Criar aplicativos Node.js Express com o Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-nodeexpressapp) +- [Centro de Desenvolvimento do Office](http://dev.office.com/) + +## Avisos de terceiros + +Consulte [Notificações de terceiros](./THIRD%20PARTY%20NOTICES) para obter mais informações sobre os pacotes que estão incluídos no [package.json](./package.json) + +## Relatórios de segurança + +Se você encontrar um problema de segurança com nossas bibliotecas ou serviços, informe-o em [secure@microsoft.com](mailto:secure@microsoft.com) com o máximo de detalhes possível. O seu envio pode estar qualificado para uma recompensa por meio do programa [Microsoft Bounty](http://aka.ms/bugbounty). Não poste problemas de segurança nos Problemas do GitHub ou qualquer outro site público. Entraremos em contato com você em breve após receber as informações. Recomendamos que você obtenha notificações sobre a ocorrência de incidentes de segurança visitando [esta página](https://technet.microsoft.com/en-us/security/dd252948) e assinando os alertas do Security Advisory. + +## Licença + +Copyright (c) Microsoft Corporation. Todos os direitos reservados. Licenciado sob a Licença MIT (a "[Licença](./LICENSE)"); + +## Valorizamos e cumprimos o Código de Conduta de Código Aberto da Microsoft + +Este projeto adotou o [Código de Conduta de Código Aberto da Microsoft](https://opensource.microsoft.com/codeofconduct/). Para saber mais, confira as [Perguntas frequentes sobre o Código de Conduta](https://opensource.microsoft.com/codeofconduct/faq/) ou entre em contato pelo [opencode@microsoft.com](mailto:opencode@microsoft.com) se tiver outras dúvidas ou comentários. diff --git a/README-Localized/README-ru-ru.md b/README-Localized/README-ru-ru.md new file mode 100644 index 000000000..e43fe89d9 --- /dev/null +++ b/README-Localized/README-ru-ru.md @@ -0,0 +1,229 @@ +# Клиентская библиотека JavaScript для Microsoft Graph + +[![Эмблема версии npm](https://img.shields.io/npm/v/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) [![Travis](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript.svg?maxAge=86400)](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript) [![Известные уязвимости](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript/badge.svg?maxAge=86400)](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript) [![Лицензия](https://img.shields.io/github/license/microsoftgraph/msgraph-sdk-javascript.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![Стиль кода: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![Загрузки](https://img.shields.io/npm/dm/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) + +Клиентская библиотека JavaScript для Microsoft Graph — это компактная оболочка API Microsoft Graph, которую можно использовать на стороне сервера и в браузере. + +**Требуется IntelliSense для моделей (пользователи, группы и т. п.)? Ознакомьтесь с репозиторием [Microsoft Graph Types](https://github.com/microsoftgraph/msgraph-typescript-typings).** + +[![Демонстрация TypeScript](https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-javascript/master/types-demo.PNG)](https://github.com/microsoftgraph/msgraph-typescript-typings) + +## Установка + +### С помощью npm + +```cmd +npm install @microsoft/microsoft-graph-client +``` + +Импортируйте `@microsoft/microsoft-graph-client` в модуль. Также потребуются полизаполнения для получения данных, например [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch). + +```typescript +import "isomorphic-fetch"; +import { Client } from "@microsoft/microsoft-graph-client"; +``` + +### С помощью тега Script + +Включите [graph-js-sdk.js](https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js) в страницу HTML. + +```HTML + +``` + +Если ваш браузер не поддерживает [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) \[[поддержка](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility)] или [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) \[[поддержка](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility)], потребуется использовать полизаполнения, например [github/fetch](https://github.com/github/fetch) для fetch и [es6-promise](https://github.com/stefanpenner/es6-promise) для promise. + +```HTML + + + + + + + + +``` + +## Начало работы + +### 1. Зарегистрируйте приложение + +Зарегистрируйте приложение для использования API Microsoft Graph с помощью одного из следующих поддерживаемых порталов проверки подлинности. + +- [Портал регистрации приложений Майкрософт](https://apps.dev.microsoft.com). Зарегистрируйте новое приложение, работающее с учетными записями Майкрософт и/или с учетными записями организации, используя объединенную конечную точку проверки подлинности версии 2. +- [Microsoft Azure Active Directory](https://manage.windowsazure.com). Зарегистрируйте новое приложение в службе каталогов Active Directory клиента для поддержки пользователей рабочих или учебных учетных записей для одного или нескольких клиентов. + +### 2. Выполните проверку подлинности для службы Microsoft Graph + +В клиентской библиотеке JavaScript для Microsoft Graph есть реализация адаптера ([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts)) для [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core) (библиотека проверки подлинности Майкрософт), который получает `accessToken`. Библиотека MSAL не поставляется вместе с этой библиотекой, пользователю потребуется включить ее извне (сведения о включении MSAL см. [здесь](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)). + +> **Важное примечание.** MSAL поддерживается только для интерфейсных приложений. Для проверки подлинности на стороне сервера вам потребуется реализовать собственный поставщик AuthenticationProvider. Узнайте, как создать [настраиваемый поставщик проверки подлинности](./docs/CustomAuthenticationProvider.md). + +#### Создание экземпляра ImplicitMSALAuthenticationProvider в среде браузера + +См. devDependencies в [package.json](./package.json) для получения сведений о совместимой версии MSAL. Обновите эту версию ниже. + +```html + +``` + +```typescript + +// Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new Msal.UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +#### Создание экземпляра ImplicitMSALAuthenticationProvider в среде узла + +См. devDependencies в [package.json](./package.json) для получения сведений о совместимой версии MSAL. Обновите эту версию ниже. + +```cmd +npm install msal@ +``` + +```typescript +import { UserAgentApplication } from "msal"; + +import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; + +// An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +Пользовать может реализовать предпочитаемую библиотеку проверки подлинности путем реализации интерфейса `IAuthenticationProvider`. См. сведения о реализации [настраиваемого поставщика проверки подлинности](./docs/CustomAuthenticationProvider.md). + +### 3. Инициализируйте клиентский объект Microsoft Graph с поставщиком проверки подлинности + +Экземпляр класса **Client** обрабатывает запросы к API Microsoft Graph и обработку ответов. Чтобы создать новый экземпляр этого класса, нужно предоставить экземпляр [`IAuthenticationProvider`](src/IAuthenticationProvider.ts), который необходимо передать в качестве значения для ключа `authProvider` в разделе [`ClientOptions`](src/IClientOptions.ts) статическому методу инициализатора `Client.initWithMiddleware`. + +#### Для среды браузера + +```typescript +const options = { + authProvider, // An instance created from previous step +}; +const Client = MicrosoftGraph.Client; +const client = Client.initWithMiddleware(options); +``` + +#### Для среды узла + +```typescript +import { Client } from "@microsoft/microsoft-graph-client"; + +const options = { + authProvider, // An instance created from previous step +}; +const client = Client.initWithMiddleware(options); +``` + +Сведения об инициализации клиента см. в [этом документе](./docs/CreatingClientInstance.md). + +### 4. Создайте запросы к Microsoft Graph + +После настройки проверки подлинности и экземпляра клиента можно начать вызывать службу. Все запросы должны начинаться с `client.api(путь)` и оканчиваться [действием](./docs/Actions.md). + +Получение сведений о пользователе + +```typescript +try { + let userDetails = await client.api("/me").get(); + console.log(userDetails); +} catch (error) { + throw error; +} +``` + +Отправка сообщения электронной почты получателям + +```typescript +// Construct email object +const mail = { + subject: "Microsoft Graph JavaScript Sample", + toRecipients: [ + { + emailAddress: { + address: "example@example.com", + }, + }, + ], + body: { + content: "

MicrosoftGraph JavaScript Sample

Check out https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +try { + let response = await client.api("/me/sendMail").post({ message: mail }); + console.log(response); +} catch (error) { + throw error; +} +``` + +Дополнительные сведения: [шаблон вызова](docs/CallingPattern.md), [действия](docs/Actions.md), [параметры запросов](docs/QueryParameters.md), [методы API](docs/OtherAPIs.md) и [прочее](docs/). + +## Документация + +- [Пакетная обработка](docs/content/Batching.md) +- [Задача отправки больших файлов](docs/tasks/LargeFileUploadTask.md) +- [Итератор страниц](docs/tasks/PageIterator.md) +- [Действия](docs/Actions.md) +- [Параметры запросов](docs/QueryParameters.md) +- [Прочие API](docs/OtherAPIs.md) +- [Получение необработанного ответа](docs/GettingRawResponse.md) + +## Вопросы и комментарии + +Мы будем рады узнать ваше мнение о клиентской библиотеке JavaScript для Microsoft Graph. Вы можете отправить нам вопросы и предложения в разделе [Проблемы](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues) этого репозитория. + +## Помощь + +См. [добавление рекомендаций](CONTRIBUTING.md). + +## Дополнительные ресурсы + +- [Веб-сайт Microsoft Graph](https://graph.microsoft.io) +- [Типы TypeScript в Microsoft Graph](https://github.com/microsoftgraph/msgraph-typescript-typings/) +- [Создание одностраничных приложений Angular с помощью Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-angularspa) +- [Создание приложений Node.js Express с помощью Microsoft Graph](https://github.com/microsoftgraph/msgraph-training-nodeexpressapp) +- [Центр разработчиков Office](http://dev.office.com/) + +## Уведомления третьих лиц + +См. раздел [Уведомления третьих лиц](./THIRD%20PARTY%20NOTICES) для получения сведений о пакетах, входящих в состав [package.json](./package.json) + +## Отчеты о безопасности + +Если вы столкнулись с проблемами безопасности наших библиотек или служб, сообщите о проблеме по адресу [secure@microsoft.com](mailto:secure@microsoft.com), добавив как можно больше деталей. Возможно, вы получите вознаграждение, в рамках программы [Microsoft Bounty](http://aka.ms/bugbounty). Не публикуйте ошибки безопасности в ошибках GitHub или на любом общедоступном сайте. Вскоре после получения информации, мы свяжемся с вами. Рекомендуем вам настроить уведомления о нарушениях безопасности. Это можно сделать, подписавшись на уведомления безопасности консультационных служб на [этой странице](https://technet.microsoft.com/en-us/security/dd252948). + +## Лицензия + +© Корпорация Майкрософт. Все права защищены. Предоставляется по лицензии MIT (далее — "[Лицензия](./LICENSE)"); + +## Мы соблюдаем правила поведения при использовании открытого кода Майкрософт. + +Этот проект соответствует [Правилам поведения разработчиков открытого кода Майкрософт](https://opensource.microsoft.com/codeofconduct/). Дополнительные сведения см. в разделе [часто задаваемых вопросов о правилах поведения](https://opensource.microsoft.com/codeofconduct/faq/). Если у вас возникли вопросы или замечания, напишите нам по адресу [opencode@microsoft.com](mailto:opencode@microsoft.com). diff --git a/README-Localized/README-zh-cn.md b/README-Localized/README-zh-cn.md new file mode 100644 index 000000000..662cac5d5 --- /dev/null +++ b/README-Localized/README-zh-cn.md @@ -0,0 +1,229 @@ +# Microsoft Graph JavaScript 客户端库 + +[![npm 版本徽章](https://img.shields.io/npm/v/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) [![Travis](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript.svg?maxAge=86400)](https://travis-ci.org/microsoftgraph/msgraph-sdk-javascript) [![已知漏洞](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript/badge.svg?maxAge=86400)](https://snyk.io/test/github/microsoftgraph/msgraph-sdk-javascript) [![许可证](https://img.shields.io/github/license/microsoftgraph/msgraph-sdk-javascript.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![代码样式:美观](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/microsoftgraph/msgraph-sdk-javascript) [![下载](https://img.shields.io/npm/dm/@microsoft/microsoft-graph-client.svg?maxAge=86400)](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) + +Microsoft Graph JavaScript 客户端库轻型 Microsoft Graph API 包装器,可在服务器侧和浏览器中使用。 + +**正常查找模型上的 IntelliSense(用户、组等)?查看 [Microsoft Graph 类型](https://github.com/microsoftgraph/msgraph-typescript-typings)存储库!** + +[![TypeScript 演示](https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-javascript/master/types-demo.PNG)](https://github.com/microsoftgraph/msgraph-typescript-typings) + +## 安装 + +### 通过 npm + +```cmd +npm install @microsoft/microsoft-graph-client +``` + +导入 `@microsoft/microsoft-graph-client` 至你的模块,同时需要填充代码如[isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch)等来进行提取。 + +```typescript +import "isomorphic-fetch"; +import { Client } from "@microsoft/microsoft-graph-client"; +``` + +### 通过脚本标记 + +包含 [graph-js-sdk.js](https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js) 至 HTML 页面。 + +```HTML + +``` + +如果你的浏览器不支持“[提取](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) \[[支持](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility)] 或[承诺](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) \[[支持](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility)],需要使用类似 [github/fetch](https://github.com/github/fetch) 的填充代码进行提取,并使用 [es6-promise](https://github.com/stefanpenner/es6-promise) 等进行承诺。 + +```HTML + + + + + + + + +``` + +## 入门 + +### 1.注册应用程序 + +使用下列支持的身份验证门户之一注册应用程序,以使用 Microsoft Graph API: + +- 转到 [Microsoft 应用程序注册门户](https://apps.dev.microsoft.com):使用统一的 V2 验证终结点来注册使用 Microsoft 账户和/或组织账户工作的新应用程序。 +- [Microsoft Azure Active Directory](https://manage.windowsazure.com):在租户 Active Directory 中注册新应用程序,以支持租户或多租户的工作或学校账户。 + +### 2.Microsoft Graph 服务身份验证 + +对于负责获取 `accessToken` 的 [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core)(Microsoft 身份验证库),Microsoft Graph JavaScript 客户端库拥有适配器实现([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts))。MSAL 库不随此库提供,用户需要外部将之包含(对于包含 MSAL,参见“[此处](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)”)。 + +> **重要说明:**MSAL 仅支持前端应用程序,对于服务器侧身份应用,需要实现自己的AuthenticationProvider。了解如何创建“[自定义身份验证提供程序](./docs/CustomAuthenticationProvider.md)”。 + +#### 在浏览器环境中创建 ImplicitMSALAuthenticationProvider 实例 + +有关兼容 msal 版本和下列版本的更新,参见[package.json](./package.json) 中的devDependencies。 + +```html + +``` + +```typescript + +// Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new Msal.UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +#### 在节点环境中创建 ImplicitMSALAuthenticationProvider 实例 + +有关兼容 msal 版本和下列版本的更新,参见[package.json](./package.json) 中的devDependencies。 + +```cmd +npm install msal@ +``` + +```typescript +import { UserAgentApplication } from "msal"; + +import { ImplicitMSALAuthenticationProvider } from "./node_modules/@microsoft/microsoft-graph-client/lib/src/ImplicitMSALAuthenticationProvider"; + +// An Optional options for initializing the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-basics#configuration-options +const msalConfig = { + auth: { + clientId: "your_client_id", // Client Id of the registered application + redirectUri: "your_redirect_uri", + }, +}; +const graphScopes = ["user.read", "mail.send"]; // An array of graph scopes + +// Important Note: This library implements loginPopup and acquireTokenPopup flow, remember this while initializing the msal +// Initialize the MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js#1-instantiate-the-useragentapplication +const msalApplication = new UserAgentApplication(msalConfig); +const options = new MicrosoftGraph.MSALAuthenticationProviderOptions(graphScopes); +const authProvider = new ImplicitMSALAuthenticationProvider(msalApplication, options); +``` + +通过实现 `IAuthenticationProvider` 接口,用户可集成自己首选的身份验证库。参见实现“[身份验证提供程序](./docs/CustomAuthenticationProvider.md)”。 + +### 3.使用身份验证提供程序初始化 Microsoft Graph 客户端 + +“**客户端**”类实例处理 Microsoft Graph API 请求并处理响应。若要创建此类的新实例,需要提供 [`IAuthenticationProvider`](src/IAuthenticationProvider.ts) 实例,它需要作为 [`ClientOptions`](src/IClientOptions.ts) 中的 `authProvider` 密钥值传递至静态初始化表达式方法 `Client.initWithMiddleware`。 + +#### 浏览器环境 + +```typescript +const options = { + authProvider, // An instance created from previous step +}; +const Client = MicrosoftGraph.Client; +const client = Client.initWithMiddleware(options); +``` + +#### 节点环境 + +```typescript +import { Client } from "@microsoft/microsoft-graph-client"; + +const options = { + authProvider, // An instance created from previous step +}; +const client = Client.initWithMiddleware(options); +``` + +有关初始化客户端的详细信息,请参阅“[此文档](./docs/CreatingClientInstance.md)”。 + +### 4.对图形发出请求 + +拥有身份验证设置和客户端实例后,可开始对服务进行调用。所有请求应使用 `client.api(path)` 开始并以 “[操作](./docs/Actions.md)”结束。 + +获取用户详情 + +```typescript +try { + let userDetails = await client.api("/me").get(); + console.log(userDetails); +} catch (error) { + throw error; +} +``` + +发送电子邮件至收件人 + +```typescript +// Construct email object +const mail = { + subject: "Microsoft Graph JavaScript Sample", + toRecipients: [ + { + emailAddress: { + address: "example@example.com", + }, + }, + ], + body: { + content: "

MicrosoftGraph JavaScript Sample

Check out https://github.com/microsoftgraph/msgraph-sdk-javascript", + contentType: "html", + }, +}; +try { + let response = await client.api("/me/sendMail").post({ message: mail }); + console.log(response); +} catch (error) { + throw error; +} +``` + +有关详细信息,请参阅:[调用模式](docs/CallingPattern.md)、 [操作](docs/Actions.md)、[查询参数](docs/QueryParameters.md)、[API 方法](docs/OtherAPIs.md) 和 [更多](docs/)。 + +## 文档 + +- [批处理](docs/content/Batching.md) +- [大型文件上传任务](docs/tasks/LargeFileUploadTask.md) +- [页面迭代器](docs/tasks/PageIterator.md) +- [操作](docs/Actions.md) +- [查询参数](docs/QueryParameters.md) +- [其他 API](docs/OtherAPIs.md) +- [获取原始响应](docs/GettingRawResponse.md) + +## 问题和意见 + +我们乐意倾听你有关 Microsoft Graph JavaScript 客户端库的反馈。你可以在该存储库中的[问题](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues)部分将问题和建议发送给我们。 + +## 参与 + +请参阅[参与指南](CONTRIBUTING.md)。 + +## 其他资源 + +- [Microsoft Graph 网站](https://graph.microsoft.io) +- [Microsoft Graph TypeScript 类型](https://github.com/microsoftgraph/msgraph-typescript-typings/) +- [使用 Microsoft Graph 生成 Angular 单页应用](https://github.com/microsoftgraph/msgraph-training-angularspa) +- [使用 Microsoft Graph 生成 Node.js Express 应用](https://github.com/microsoftgraph/msgraph-training-nodeexpressapp) +- [Office 开发人员中心](http://dev.office.com/) + +## 第三方声明 + +有关 [package.json](./package.json) 包含的程序包信息,参见“[第三方声明](./THIRD%20PARTY%20NOTICES)” + +## 安全报告 + +如果发现库或服务存在安全问题,请尽可能详细地报告至 [secure@microsoft.com](mailto:secure@microsoft.com)。提交可能有资格通过 [Microsoft 报告奖励](http://aka.ms/bugbounty)计划获得奖励。请勿发布安全问题至 GitHub 问题或其他任何公共网站。我们将在收到信息后立即与你联系。建议发生安全事件时获取相关通知,方法是访问[此页](https://technet.microsoft.com/en-us/security/dd252948)并订阅“安全公告通知”。 + +## 许可证 + +版权所有 (c) Microsoft Corporation。保留所有权利。在 MIT 许可证(“[许可证](./LICENSE)”)下获得许可。 + +## 我们重视并遵守“Microsoft 开放源代码行为准则” + +此项目已采用 [Microsoft 开放源代码行为准则](https://opensource.microsoft.com/codeofconduct/)。有关详细信息,请参阅[行为准则常见问题解答](https://opensource.microsoft.com/codeofconduct/faq/)。如有其他任何问题或意见,也可联系 [opencode@microsoft.com](mailto:opencode@microsoft.com)。 From 363d4ab7d587c38e5d15774cd8d1a7c351e6033e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 02:28:06 -0700 Subject: [PATCH 47/55] Bump node-fetch from 2.5.0 to 2.6.1 in /spec (#323) Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.5.0 to 2.6.1. - [Release notes](https://github.com/bitinn/node-fetch/releases) - [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/bitinn/node-fetch/compare/v2.5.0...v2.6.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: nikithauc --- spec/package-lock.json | 6 +++--- spec/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/package-lock.json b/spec/package-lock.json index eb27027e5..ac99494c4 100644 --- a/spec/package-lock.json +++ b/spec/package-lock.json @@ -211,9 +211,9 @@ } }, "node-fetch": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.5.0.tgz", - "integrity": "sha512-YuZKluhWGJwCcUu4RlZstdAxr8bFfOVHakc1mplwHkk8J+tqM1Y5yraYvIUpeX8aY7+crCwiELJq7Vl0o0LWXw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "pathval": { diff --git a/spec/package.json b/spec/package.json index b1f689ed0..97111cb74 100644 --- a/spec/package.json +++ b/spec/package.json @@ -12,6 +12,6 @@ "form-data": "^2.3.3", "isomorphic-fetch": "^2.2.1", "msal": "^1.0.0", - "node-fetch": "^2.5.0" + "node-fetch": "^2.6.1" } } From 2c97e58eab41c6153e1154f9dbbef4afa71b3a04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 02:35:37 -0700 Subject: [PATCH 48/55] Bump lodash from 4.17.11 to 4.17.19 (#300) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.19) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vincent Biret Co-authored-by: nikithauc --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d4e5120d..68ca7ee26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4062,9 +4062,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "log-symbols": { From 66a41814cace93cf1da8d5ead69b8df86c1881b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 02:42:06 -0700 Subject: [PATCH 49/55] Bump acorn from 6.1.1 to 6.4.1 (#273) Bumps [acorn](https://github.com/acornjs/acorn) from 6.1.1 to 6.4.1. - [Release notes](https://github.com/acornjs/acorn/releases) - [Commits](https://github.com/acornjs/acorn/compare/6.1.1...6.4.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vincent Biret Co-authored-by: nikithauc --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68ca7ee26..74ef87ee5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -819,9 +819,9 @@ } }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, "ansi-colors": { From 2ac83f18829a8f0353f1741108e75571d287f2a6 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Fri, 23 Oct 2020 02:10:04 -0700 Subject: [PATCH 50/55] Nikithauc/merge release 2.1.0 dev (#345) * FIx Batching Documentation code The code example in the Batching Documentation does not work. The batchResponse is not set in the example code. * Correcting missing import, incorrect reference in example (#326) * Release - 2.1.0 (#338) * updated broken link in README * Post request with empty body, now working fine. * updated the tests for this case * made change to use undefined instead of null as classname * added functionality to simplify building middleware chain * Updated GraphRequest.ts for .count() scenario Updated GraphRequest.ts to handle .count() when no parameter is specified * Bumped version to '2.1.0-Preview.1' * Adding the Modifying middleware chain samples * - fixes broken link to client instance * Updated the example to the correct return type * fix casing on filename * Update package.json * Designed ChaosHandler * Removing the set and get middleware chain method * Update README * Update README * Bump lodash from 4.17.15 to 4.17.19 in /samples/browser Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] * Changed parsepath and query functions to split query params on first equals sign considering nested queries. Added comments to query functions * Correcting spelling in comments, setting the param key directlyand replacing ternary condition with if else * Adding a buildheaders function to conditionally set content type and checking if body is null when serializing content * Altering condition to be optimal Co-authored-by: Mustafa Zengin * Adding formdata to the node project, setting the esModuleInterop config as true for allowing import of npm modules, adding unit test for application/xhtml+xml content type post * Adding a private query parsing function and more validations * Adding tests for url parsing and comments to the private url parsing functions * Adding more information in the return comments for functions returning the GraphRequest instance * Adding unit test for serializecontent testing if content is null, changed content type setting for put and patch * Changing the conditional expression to check for undefined or null headers Co-authored-by: Mustafa Zengin * Adding the missing return Co-authored-by: Mustafa Zengin * Removing the try catch block from the test case * Removing the try catch block from the test case * Returning if the content-type is present else setting content-type as default * Uninstalling formdata, restoring the tsconfigs and removing the condition to check if form-data is undefined in GraphRequest.ts * Restoring file * Reverting the package json changes * Bump yargs-parser from 13.1.1 to 13.1.2 in /samples/browser (#321) Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 13.1.1 to 13.1.2. - [Release notes](https://github.com/yargs/yargs-parser/releases) - [Changelog](https://github.com/yargs/yargs-parser/blob/master/docs/CHANGELOG-full.md) - [Commits](https://github.com/yargs/yargs-parser/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump mixin-deep from 1.3.1 to 1.3.2 in /scripts (#301) Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/jonschlinkert/mixin-deep/releases) - [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vincent Biret Co-authored-by: nikithauc * Bump http-proxy from 1.17.0 to 1.18.1 in /samples/browser (#319) Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/http-party/node-http-proxy/releases) - [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md) - [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update OneDriveLargeFileUploadTask.ts (#325) * Update OneDriveLargeFileUploadTask.ts Trying to upload files with the '#' or '%' character in the filename ends with network errors of the form - 400 status response "The parameter item does not exist in method getByPath" Encoding the entire URL does not encode URL components correctly. When the SDK's client tries to encode these URL components in the parameters on the call to OneDriveLargeFileUploadTask.create, then the resultant file in OneDrive has the URL encoded filename (as opposed to the user-friendly/decoded filename). Therefore, fix the encoding in the SDK's URL components itself, directly. * Update OneDriveLargeFileUploadTask.ts Fix typo * Update src/tasks/OneDriveLargeFileUploadTask.ts Updated comment Co-authored-by: Mustafa Zengin * Update OneDriveLargeFileUploadTask.ts Updated comment Co-authored-by: Mustafa Zengin * Use correct class name (#284) Co-authored-by: Vincent Biret Co-authored-by: nikithauc * Typo in documentation example. (#270) Co-authored-by: Vincent Biret Co-authored-by: nikithauc * Enhancement/#311 page iterator request options (#318) * Adding request options property to PageIterator * Adding tests for page iterator task to test passing along the headers * using headersinit type for headers * Specifying parameter definition * using constants * Updating function documentation * remove response type, test passing fetchoptions * testing requestOptions set in pageiterator * typos, optional parameters comments * Sorting parameter list - chaoshandleroptions (#334) * 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 * merging with dev * Using spread operator * Checking if middleware is not empty Co-authored-by: Vincent Biret * Make GraphError real Error (#335) Co-authored-by: nikithauc * Using ternary shorthand, upgrade preview 2.1.0-2 (#337) * Bump version to 2.1.0 * changing.substring parameter, comments to check middleware * adding tests, urlotherqueryoptions type Co-authored-by: Abhinav Srivastava Co-authored-by: muthurathinam Co-authored-by: Vincent Biret Co-authored-by: warreee Co-authored-by: Gideon Goldberg Co-authored-by: Muthurathinam <6259786+muthurathinam@users.noreply.github.com> Co-authored-by: Nikola Metulev Co-authored-by: Behnam Mohammadi Co-authored-by: DeVere Dyett Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mustafa Zengin Co-authored-by: Michael Mainer Co-authored-by: Vincent Biret Co-authored-by: Hari Sridharan Co-authored-by: Mustafa Zengin Co-authored-by: mattdenkers Co-authored-by: lewgordon <50742795+lewgordon@users.noreply.github.com> Co-authored-by: Olivier Cuypers Co-authored-by: Abrax20 Co-authored-by: Darrel Co-authored-by: Abhinav Srivastava Co-authored-by: muthurathinam Co-authored-by: Vincent Biret Co-authored-by: warreee Co-authored-by: Gideon Goldberg Co-authored-by: Muthurathinam <6259786+muthurathinam@users.noreply.github.com> Co-authored-by: Nikola Metulev Co-authored-by: Behnam Mohammadi Co-authored-by: DeVere Dyett Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mustafa Zengin Co-authored-by: Michael Mainer Co-authored-by: Vincent Biret Co-authored-by: Hari Sridharan Co-authored-by: Mustafa Zengin Co-authored-by: mattdenkers Co-authored-by: lewgordon <50742795+lewgordon@users.noreply.github.com> Co-authored-by: Olivier Cuypers --- README-Localized/README-es-es.md | 1 - README-Localized/README-fr-fr.md | 1 - README-Localized/README-ja-jp.md | 1 - README-Localized/README-pt-br.md | 3 +-- README-Localized/README-ru-ru.md | 1 - README-Localized/README-zh-cn.md | 7 +++---- spec/core/urlParsing.ts | 6 +++++- src/GraphRequest.ts | 12 +++++------- src/HTTPClient.ts | 2 ++ 9 files changed, 16 insertions(+), 18 deletions(-) diff --git a/README-Localized/README-es-es.md b/README-Localized/README-es-es.md index de872586d..97ef0641a 100644 --- a/README-Localized/README-es-es.md +++ b/README-Localized/README-es-es.md @@ -68,7 +68,6 @@ Consulte devDependencies en [package.json](./package.json) para ver la versión ``` ```typescript - // Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options const msalConfig = { auth: { diff --git a/README-Localized/README-fr-fr.md b/README-Localized/README-fr-fr.md index 465e6a911..fb16cc028 100644 --- a/README-Localized/README-fr-fr.md +++ b/README-Localized/README-fr-fr.md @@ -68,7 +68,6 @@ Reportez-vous à devDependencies dans [package.json](./package.json) pour la ver ``` ```typescript - // Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options const msalConfig = { auth: { diff --git a/README-Localized/README-ja-jp.md b/README-Localized/README-ja-jp.md index 9c347ef83..f5d1baaee 100644 --- a/README-Localized/README-ja-jp.md +++ b/README-Localized/README-ja-jp.md @@ -68,7 +68,6 @@ Microsoft Graph JavaScript クライアント ライブラリには、`accessTok ``` ```typescript - // Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options const msalConfig = { auth: { diff --git a/README-Localized/README-pt-br.md b/README-Localized/README-pt-br.md index 1ee408fba..7bf45d545 100644 --- a/README-Localized/README-pt-br.md +++ b/README-Localized/README-pt-br.md @@ -68,7 +68,6 @@ Consulte devDependencies no [package.json](./package.json) para a versão msal c ``` ```typescript - // Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options const msalConfig = { auth: { @@ -226,4 +225,4 @@ Copyright (c) Microsoft Corporation. Todos os direitos reservados. Licenciado so ## Valorizamos e cumprimos o Código de Conduta de Código Aberto da Microsoft -Este projeto adotou o [Código de Conduta de Código Aberto da Microsoft](https://opensource.microsoft.com/codeofconduct/). Para saber mais, confira as [Perguntas frequentes sobre o Código de Conduta](https://opensource.microsoft.com/codeofconduct/faq/) ou entre em contato pelo [opencode@microsoft.com](mailto:opencode@microsoft.com) se tiver outras dúvidas ou comentários. +Este projeto adotou o [Código de Conduta de Código Aberto da Microsoft](https://opensource.microsoft.com/codeofconduct/). Para saber mais, confira as [Perguntas frequentes sobre o Código de Conduta](https://opensource.microsoft.com/codeofconduct/faq/) ou entre em contato pelo [opencode@microsoft.com](mailto:opencode@microsoft.com) se tiver outras dúvidas ou comentários. diff --git a/README-Localized/README-ru-ru.md b/README-Localized/README-ru-ru.md index e43fe89d9..cab82c2b8 100644 --- a/README-Localized/README-ru-ru.md +++ b/README-Localized/README-ru-ru.md @@ -68,7 +68,6 @@ import { Client } from "@microsoft/microsoft-graph-client"; ``` ```typescript - // Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options const msalConfig = { auth: { diff --git a/README-Localized/README-zh-cn.md b/README-Localized/README-zh-cn.md index 662cac5d5..12a835fef 100644 --- a/README-Localized/README-zh-cn.md +++ b/README-Localized/README-zh-cn.md @@ -57,18 +57,17 @@ import { Client } from "@microsoft/microsoft-graph-client"; 对于负责获取 `accessToken` 的 [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core)(Microsoft 身份验证库),Microsoft Graph JavaScript 客户端库拥有适配器实现([ImplicitMSALAuthenticationProvider](src/ImplicitMSALAuthenticationProvider.ts))。MSAL 库不随此库提供,用户需要外部将之包含(对于包含 MSAL,参见“[此处](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-core#installation)”)。 -> **重要说明:**MSAL 仅支持前端应用程序,对于服务器侧身份应用,需要实现自己的AuthenticationProvider。了解如何创建“[自定义身份验证提供程序](./docs/CustomAuthenticationProvider.md)”。 +> **重要说明:**MSAL 仅支持前端应用程序,对于服务器侧身份应用,需要实现自己的 AuthenticationProvider。了解如何创建“[自定义身份验证提供程序](./docs/CustomAuthenticationProvider.md)”。 #### 在浏览器环境中创建 ImplicitMSALAuthenticationProvider 实例 -有关兼容 msal 版本和下列版本的更新,参见[package.json](./package.json) 中的devDependencies。 +有关兼容 msal 版本和下列版本的更新,参见[package.json](./package.json) 中的 devDependencies。 ```html ``` ```typescript - // Configuration options for MSAL @see https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options const msalConfig = { auth: { @@ -87,7 +86,7 @@ const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalA #### 在节点环境中创建 ImplicitMSALAuthenticationProvider 实例 -有关兼容 msal 版本和下列版本的更新,参见[package.json](./package.json) 中的devDependencies。 +有关兼容 msal 版本和下列版本的更新,参见[package.json](./package.json) 中的 devDependencies。 ```cmd npm install msal@ diff --git a/spec/core/urlParsing.ts b/spec/core/urlParsing.ts index e73dea023..c1241eac4 100644 --- a/spec/core/urlParsing.ts +++ b/spec/core/urlParsing.ts @@ -37,8 +37,12 @@ const testCases = { "/items?$expand=fields($select=Title)&$expand=name($select=firstName)": "https://graph.microsoft.com/v1.0/items?$expand=fields($select=Title),name($select=firstName)", // Passing invalid parameters - "/me?&test&123": "https://graph.microsoft.com/v1.0/me?&test&123", + "/me?test&123": "https://graph.microsoft.com/v1.0/me?test&123", "/me?$select($select=name)": "https://graph.microsoft.com/v1.0/me?$select($select=name)", + "/me/?$filter=any(Actors, Name eq 'John Belushi')": "https://graph.microsoft.com/v1.0/me/?$filter=any(Actors, Name eq 'John Belushi')", + "/me/$filter=any(Actors, it/ID eq Director/ID)": "https://graph.microsoft.com/v1.0/me/$filter=any(Actors, it/ID eq Director/ID)", + "/me?$whatif": "https://graph.microsoft.com/v1.0/me?$whatif", + "/me/?$filter=any(Actors a, any(a/Movies m, a/ID eq m/Director/ID))": "https://graph.microsoft.com/v1.0/me/?$filter=any(Actors a, any(a/Movies m, a/ID eq m/Director/ID))", }; describe("urlParsing.ts", () => { diff --git a/src/GraphRequest.ts b/src/GraphRequest.ts index 2077e5652..7ec736be1 100644 --- a/src/GraphRequest.ts +++ b/src/GraphRequest.ts @@ -41,6 +41,7 @@ interface KeyValuePairObjectStringNumber { * @property {string} [path] - The path of the resource request * @property {KeyValuePairObjectStringNumber} oDataQueryParams - The oData Query Params * @property {KeyValuePairObjectStringNumber} otherURLQueryParams - The other query params for a request + * @property {string[]} otherURLQueryOptions - The non key-value query parameters. Example- '/me?$whatif' */ export interface URLComponents { host: string; @@ -48,7 +49,7 @@ export interface URLComponents { path?: string; oDataQueryParams: KeyValuePairObjectStringNumber; otherURLQueryParams: KeyValuePairObjectStringNumber; - otherURLQueryOptions: any[]; + otherURLQueryOptions?: string[]; } /** @@ -254,7 +255,7 @@ export class GraphRequest { private parseQueryParameter(queryDictionaryOrString: string | KeyValuePairObjectStringNumber): GraphRequest { if (typeof queryDictionaryOrString === "string") { if (queryDictionaryOrString.charAt(0) === "?") { - queryDictionaryOrString = queryDictionaryOrString.substring(1, queryDictionaryOrString.length); + queryDictionaryOrString = queryDictionaryOrString.substring(1); } if (queryDictionaryOrString.indexOf("&") !== -1) { @@ -271,9 +272,6 @@ export class GraphRequest { this.setURLComponentsQueryParamater(key, queryDictionaryOrString[key]); } } - } else { - /*Push values which are not of key-value structure. - Example-> Handle an invalid input->.query(123) and let the Graph API respond with the error in the URL*/ this.urlComponents.otherURLQueryOptions.push(queryDictionaryOrString); } return this; @@ -291,10 +289,10 @@ export class GraphRequest { if (this.isValidQueryKeyValuePair(queryParameter)) { const indexOfFirstEquals = queryParameter.indexOf("="); const paramKey = queryParameter.substring(0, indexOfFirstEquals); - const paramValue = queryParameter.substring(indexOfFirstEquals + 1, queryParameter.length); + const paramValue = queryParameter.substring(indexOfFirstEquals + 1); this.setURLComponentsQueryParamater(paramKey, paramValue); } else { - /* Push values which are not of key-value structure. + /* Push values which are not of key-value structure. Example-> Handle an invalid input->.query(test), .query($select($select=name)) and let the Graph API respond with the error in the URL*/ this.urlComponents.otherURLQueryOptions.push(queryParameter); } diff --git a/src/HTTPClient.ts b/src/HTTPClient.ts index b61dd06ef..c4e866d95 100644 --- a/src/HTTPClient.ts +++ b/src/HTTPClient.ts @@ -42,6 +42,7 @@ export class HTTPClient { /** * @private * Processes the middleware parameter passed to set this.middleware property + * The calling function should validate if middleware is not undefined or not empty. * @param {...Middleware} middleware - The middleware passed * @returns Nothing */ @@ -57,6 +58,7 @@ export class HTTPClient { * @private * Processes the middleware array to construct the chain * and sets this.middleware property to the first middlware handler of the array + * The calling function should validate if middleware is not undefined or not empty * @param {Middleware[]} middlewareArray - The array of middleware handlers * @returns Nothing */ From a745e316b12182426ce0e01ef536cd671351b209 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Fri, 30 Oct 2020 13:07:41 -0700 Subject: [PATCH 51/55] Adding missing exports (#348) --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index d0250f0e3..b4df6c5e3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,5 +40,7 @@ export * from "./IClientOptions"; export * from "./IContext"; export * from "./IFetchOptions"; export * from "./IGraphRequestCallback"; +export * from "./ImplicitMSALAuthenticationProvider"; export * from "./IOptions"; +export * from "./MSALAuthenticationProviderOptions"; export * from "./ResponseType"; From daa00ca1c3e9b203733a9de82960aa5132e263a0 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Wed, 11 Nov 2020 15:45:45 -0800 Subject: [PATCH 52/55] Adding/Deleting authorization and telemetry headers (#351) * Remove Graph headers for non graph urls * removing telemetry information if non graph url * Adding comments * iterable set constructor * remove try catch, remove check before deletion --- spec/core/urlParsing.ts | 4 + spec/middleware/TelemetryHandler.ts | 122 ++++++++++++++-------------- src/Constants.ts | 6 ++ src/GraphRequestUtil.ts | 33 +++++++- src/middleware/RedirectHandler.ts | 2 +- src/middleware/TelemetryHandler.ts | 40 +++++---- 6 files changed, 130 insertions(+), 77 deletions(-) diff --git a/spec/core/urlParsing.ts b/spec/core/urlParsing.ts index c1241eac4..ee47ab2bf 100644 --- a/spec/core/urlParsing.ts +++ b/spec/core/urlParsing.ts @@ -43,6 +43,10 @@ const testCases = { "/me/$filter=any(Actors, it/ID eq Director/ID)": "https://graph.microsoft.com/v1.0/me/$filter=any(Actors, it/ID eq Director/ID)", "/me?$whatif": "https://graph.microsoft.com/v1.0/me?$whatif", "/me/?$filter=any(Actors a, any(a/Movies m, a/ID eq m/Director/ID))": "https://graph.microsoft.com/v1.0/me/?$filter=any(Actors a, any(a/Movies m, a/ID eq m/Director/ID))", + + // National cloud deployment urls + "https://graph.microsoft.us/v1.0/me": "https://graph.microsoft.us/v1.0/me", + "https://dod-graph.microsoft.us/beta/me?$filter=a": "https://dod-graph.microsoft.us/beta/me?$filter=a", }; describe("urlParsing.ts", () => { diff --git a/spec/middleware/TelemetryHandler.ts b/spec/middleware/TelemetryHandler.ts index 81f5c7809..101a9ef5d 100644 --- a/spec/middleware/TelemetryHandler.ts +++ b/spec/middleware/TelemetryHandler.ts @@ -7,6 +7,7 @@ import { assert } from "chai"; +import { GRAPH_BASE_URL } from "../../src/Constants"; import { Context } from "../../src/IContext"; import { MiddlewareControl } from "../../src/middleware/MiddlewareControl"; import { FeatureUsageFlag, TelemetryHandlerOptions } from "../../src/middleware/options/TelemetryHandlerOptions"; @@ -26,79 +27,80 @@ describe("TelemetryHandler.ts", () => { statusText: "OK", }); it("Should not disturb client-request-id in the header", async () => { - try { - const uuid = "dummy_uuid"; - const context: Context = { - request: "url", - options: { - headers: { - "client-request-id": uuid, - }, + const uuid = "dummy_uuid"; + const context: Context = { + request: GRAPH_BASE_URL, + options: { + headers: { + "client-request-id": uuid, }, - }; - dummyHTTPHandler.setResponses([okayResponse]); - await telemetryHandler.execute(context); - assert.equal(context.options.headers["client-request-id"], uuid); - } catch (error) { - throw error; - } + }, + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.equal(context.options.headers["client-request-id"], uuid); }); it("Should create client-request-id if one is not present in the request header", async () => { - try { - const context: Context = { - request: "url", - options: { - headers: { - method: "GET", - }, + const context: Context = { + request: "https://GRAPH.microsoft.com:443/", + options: { + headers: { + method: "GET", }, - }; - dummyHTTPHandler.setResponses([okayResponse]); - await telemetryHandler.execute(context); - assert.isDefined(context.options.headers["client-request-id"]); - } catch (error) { - throw error; - } + }, + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.isDefined(context.options.headers["client-request-id"]); }); it("Should set sdk version header without feature flag usage if telemetry options is not present", async () => { - try { - const context: Context = { - request: "url", - options: { - headers: { - method: "GET", - }, + const context: Context = { + request: GRAPH_BASE_URL, + options: { + headers: { + method: "GET", }, - }; - dummyHTTPHandler.setResponses([okayResponse]); - await telemetryHandler.execute(context); - assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION}`); - } catch (error) { - throw error; - } + }, + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION}`); }); it("Should set sdk version header with feature flag", async () => { - try { - const telemetryOptions = new TelemetryHandlerOptions(); - telemetryOptions["setFeatureUsage"](FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED); - const context: Context = { - request: "url", - options: { - headers: { - method: "GET", - }, + const telemetryOptions = new TelemetryHandlerOptions(); + telemetryOptions["setFeatureUsage"](FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED); + const context: Context = { + request: GRAPH_BASE_URL, + options: { + headers: { + method: "GET", }, - middlewareControl: new MiddlewareControl([telemetryOptions]), - }; - dummyHTTPHandler.setResponses([okayResponse]); - await telemetryHandler.execute(context); - assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION} (featureUsage=${FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED.toString(16)})`); - } catch (error) { - throw error; - } + }, + middlewareControl: new MiddlewareControl([telemetryOptions]), + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION} (featureUsage=${FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED.toString(16)})`); + }); + + it("Should not set telemetry for non-graph url", async () => { + const context: Context = { + request: "test url", + options: { + headers: { + method: "GET", + }, + }, + middlewareControl: new MiddlewareControl(), + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.equal(context.options.headers["client-request-id"], undefined); + assert.equal(context.options.headers["SdkVersion"], undefined); + assert.equal(context.options.headers["setFeatureUsage"], undefined); }); }); /* tslint:enable: no-string-literal */ diff --git a/src/Constants.ts b/src/Constants.ts index d22efcf74..06028da66 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -20,3 +20,9 @@ export const GRAPH_API_VERSION = "v1.0"; * A Default base url for a request */ export const GRAPH_BASE_URL = "https://graph.microsoft.com/"; + +/** + * To hold list of the service root endpoints for Microsoft Graph and Graph Explorer for each national cloud. + * Set(iterable:Object) is not supported in Internet Explorer. The consumer is recommended to use a suitable polyfill. + */ +export const GRAPH_URLS = new Set(["graph.microsoft.com", "graph.microsoft.us", "dod-graph.microsoft.us", "graph.microsoft.de", "microsoftgraph.chinacloudapi.cn"]); diff --git a/src/GraphRequestUtil.ts b/src/GraphRequestUtil.ts index a9ab02bb1..78e51f021 100644 --- a/src/GraphRequestUtil.ts +++ b/src/GraphRequestUtil.ts @@ -8,7 +8,7 @@ /** * @module GraphRequestUtil */ - +import { GRAPH_URLS } from "./Constants"; /** * To hold list of OData query params */ @@ -58,3 +58,34 @@ export const serializeContent = (content: any): any => { } return content; }; + +/** + * Checks if the url is one of the service root endpoints for Microsoft Graph and Graph Explorer. + * @param {string} url - The url to be verified + * @returns {boolean} - Returns true if the url is a Graph URL + */ +export const isGraphURL = (url: string): boolean => { + // Valid Graph URL pattern - https://graph.microsoft.com/{version}/{resource}?{query-parameters} + // Valid Graph URL example - https://graph.microsoft.com/v1.0/ + url = url.toLowerCase(); + + if (url.indexOf("https://") !== -1) { + url = url.replace("https://", ""); + + // Find where the host ends + const startofPortNoPos = url.indexOf(":"); + const endOfHostStrPos = url.indexOf("/"); + let hostName = ""; + if (endOfHostStrPos !== -1) { + if (startofPortNoPos !== -1 && startofPortNoPos < endOfHostStrPos) { + hostName = url.substring(0, startofPortNoPos); + return GRAPH_URLS.has(hostName); + } + // Parse out the host + hostName = url.substring(0, endOfHostStrPos); + return GRAPH_URLS.has(hostName); + } + } + + return false; +}; diff --git a/src/middleware/RedirectHandler.ts b/src/middleware/RedirectHandler.ts index 4bdcc9e39..1d451fc7f 100644 --- a/src/middleware/RedirectHandler.ts +++ b/src/middleware/RedirectHandler.ts @@ -202,7 +202,7 @@ export class RedirectHandler implements Middleware { } else { const redirectUrl: string = this.getLocationHeader(response); if (!this.isRelativeURL(redirectUrl) && this.shouldDropAuthorizationHeader(response.url, redirectUrl)) { - setRequestHeader(context.request, context.options, RedirectHandler.AUTHORIZATION_HEADER, undefined); + delete context.options.headers[RedirectHandler.AUTHORIZATION_HEADER]; } await this.updateRequestUrl(redirectUrl, context); } diff --git a/src/middleware/TelemetryHandler.ts b/src/middleware/TelemetryHandler.ts index e72d2c187..75b8b77ca 100644 --- a/src/middleware/TelemetryHandler.ts +++ b/src/middleware/TelemetryHandler.ts @@ -8,7 +8,7 @@ /** * @module TelemetryHandler */ - +import { isGraphURL } from "../GraphRequestUtil"; import { Context } from "../IContext"; import { PACKAGE_VERSION } from "../Version"; @@ -66,21 +66,31 @@ export class TelemetryHandler implements Middleware { */ public async execute(context: Context): Promise { try { - let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER); - if (clientRequestId === null) { - clientRequestId = generateUUID(); - setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId); - } - let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`; - let options: TelemetryHandlerOptions; - if (context.middlewareControl instanceof MiddlewareControl) { - options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions; - } - if (typeof options !== "undefined") { - const featureUsage: string = options.getFeatureUsage(); - sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`; + if (typeof context.request === "string") { + if (isGraphURL(context.request)) { + // Add telemetry only if the request url is a Graph URL. + // Errors are reported as in issue #265 if headers are present when redirecting to a non Graph URL + let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER); + if (clientRequestId === null) { + clientRequestId = generateUUID(); + setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId); + } + let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`; + let options: TelemetryHandlerOptions; + if (context.middlewareControl instanceof MiddlewareControl) { + options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions; + } + if (options) { + const featureUsage: string = options.getFeatureUsage(); + sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`; + } + appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue); + } else { + // Remove telemetry headers if present during redirection. + delete context.options.headers[TelemetryHandler.CLIENT_REQUEST_ID_HEADER]; + delete context.options.headers[TelemetryHandler.SDK_VERSION_HEADER]; + } } - appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue); return await this.nextMiddleware.execute(context); } catch (error) { throw error; From 3372ee06218fdf2efe1de01cc63100487b319020 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Wed, 18 Nov 2020 11:25:01 -0800 Subject: [PATCH 53/55] Preview release/2.2.0 preview.1 (#356) * FIx Batching Documentation code The code example in the Batching Documentation does not work. The batchResponse is not set in the example code. * Correcting missing import, incorrect reference in example (#326) * Release - 2.1.0 (#338) * updated broken link in README * Post request with empty body, now working fine. * updated the tests for this case * made change to use undefined instead of null as classname * added functionality to simplify building middleware chain * Updated GraphRequest.ts for .count() scenario Updated GraphRequest.ts to handle .count() when no parameter is specified * Bumped version to '2.1.0-Preview.1' * Adding the Modifying middleware chain samples * - fixes broken link to client instance * Updated the example to the correct return type * fix casing on filename * Update package.json * Designed ChaosHandler * Removing the set and get middleware chain method * Update README * Update README * Bump lodash from 4.17.15 to 4.17.19 in /samples/browser Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] * Changed parsepath and query functions to split query params on first equals sign considering nested queries. Added comments to query functions * Correcting spelling in comments, setting the param key directlyand replacing ternary condition with if else * Adding a buildheaders function to conditionally set content type and checking if body is null when serializing content * Altering condition to be optimal Co-authored-by: Mustafa Zengin * Adding formdata to the node project, setting the esModuleInterop config as true for allowing import of npm modules, adding unit test for application/xhtml+xml content type post * Adding a private query parsing function and more validations * Adding tests for url parsing and comments to the private url parsing functions * Adding more information in the return comments for functions returning the GraphRequest instance * Adding unit test for serializecontent testing if content is null, changed content type setting for put and patch * Changing the conditional expression to check for undefined or null headers Co-authored-by: Mustafa Zengin * Adding the missing return Co-authored-by: Mustafa Zengin * Removing the try catch block from the test case * Removing the try catch block from the test case * Returning if the content-type is present else setting content-type as default * Uninstalling formdata, restoring the tsconfigs and removing the condition to check if form-data is undefined in GraphRequest.ts * Restoring file * Reverting the package json changes * Bump yargs-parser from 13.1.1 to 13.1.2 in /samples/browser (#321) Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 13.1.1 to 13.1.2. - [Release notes](https://github.com/yargs/yargs-parser/releases) - [Changelog](https://github.com/yargs/yargs-parser/blob/master/docs/CHANGELOG-full.md) - [Commits](https://github.com/yargs/yargs-parser/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump mixin-deep from 1.3.1 to 1.3.2 in /scripts (#301) Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/jonschlinkert/mixin-deep/releases) - [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vincent Biret Co-authored-by: nikithauc * Bump http-proxy from 1.17.0 to 1.18.1 in /samples/browser (#319) Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/http-party/node-http-proxy/releases) - [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md) - [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update OneDriveLargeFileUploadTask.ts (#325) * Update OneDriveLargeFileUploadTask.ts Trying to upload files with the '#' or '%' character in the filename ends with network errors of the form - 400 status response "The parameter item does not exist in method getByPath" Encoding the entire URL does not encode URL components correctly. When the SDK's client tries to encode these URL components in the parameters on the call to OneDriveLargeFileUploadTask.create, then the resultant file in OneDrive has the URL encoded filename (as opposed to the user-friendly/decoded filename). Therefore, fix the encoding in the SDK's URL components itself, directly. * Update OneDriveLargeFileUploadTask.ts Fix typo * Update src/tasks/OneDriveLargeFileUploadTask.ts Updated comment Co-authored-by: Mustafa Zengin * Update OneDriveLargeFileUploadTask.ts Updated comment Co-authored-by: Mustafa Zengin * Use correct class name (#284) Co-authored-by: Vincent Biret Co-authored-by: nikithauc * Typo in documentation example. (#270) Co-authored-by: Vincent Biret Co-authored-by: nikithauc * Enhancement/#311 page iterator request options (#318) * Adding request options property to PageIterator * Adding tests for page iterator task to test passing along the headers * using headersinit type for headers * Specifying parameter definition * using constants * Updating function documentation * remove response type, test passing fetchoptions * testing requestOptions set in pageiterator * typos, optional parameters comments * Sorting parameter list - chaoshandleroptions (#334) * 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 * merging with dev * Using spread operator * Checking if middleware is not empty Co-authored-by: Vincent Biret * Make GraphError real Error (#335) Co-authored-by: nikithauc * Using ternary shorthand, upgrade preview 2.1.0-2 (#337) * Bump version to 2.1.0 * changing.substring parameter, comments to check middleware * adding tests, urlotherqueryoptions type Co-authored-by: Abhinav Srivastava Co-authored-by: muthurathinam Co-authored-by: Vincent Biret Co-authored-by: warreee Co-authored-by: Gideon Goldberg Co-authored-by: Muthurathinam <6259786+muthurathinam@users.noreply.github.com> Co-authored-by: Nikola Metulev Co-authored-by: Behnam Mohammadi Co-authored-by: DeVere Dyett Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mustafa Zengin Co-authored-by: Michael Mainer Co-authored-by: Vincent Biret Co-authored-by: Hari Sridharan Co-authored-by: Mustafa Zengin Co-authored-by: mattdenkers Co-authored-by: lewgordon <50742795+lewgordon@users.noreply.github.com> Co-authored-by: Olivier Cuypers * Setting GraphError prototype explicity. Version 2.1.1 (#347) Co-authored-by: Abrax20 Co-authored-by: Darrel Co-authored-by: Abhinav Srivastava Co-authored-by: muthurathinam Co-authored-by: Vincent Biret Co-authored-by: warreee Co-authored-by: Gideon Goldberg Co-authored-by: Muthurathinam <6259786+muthurathinam@users.noreply.github.com> Co-authored-by: Nikola Metulev Co-authored-by: Behnam Mohammadi Co-authored-by: DeVere Dyett Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mustafa Zengin Co-authored-by: Michael Mainer Co-authored-by: Vincent Biret Co-authored-by: Hari Sridharan Co-authored-by: Mustafa Zengin Co-authored-by: mattdenkers Co-authored-by: lewgordon <50742795+lewgordon@users.noreply.github.com> Co-authored-by: Olivier Cuypers --- package-lock.json | 2 +- package.json | 2 +- spec/core/GraphErrorHandler.ts | 7 +++++++ src/GraphError.ts | 2 ++ src/Version.ts | 2 +- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74ef87ee5..09a299ceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.1.0-Preview.2", + "version": "2.2.0-Preview.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 752a6da6f..f9b1dda8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.1.0-Preview.2", + "version": "2.2.0-Preview.1", "description": "Microsoft Graph Client Library", "license": "MIT", "main": "lib/src/index.js", diff --git a/spec/core/GraphErrorHandler.ts b/spec/core/GraphErrorHandler.ts index e16c05a4a..f2be2abdf 100644 --- a/spec/core/GraphErrorHandler.ts +++ b/spec/core/GraphErrorHandler.ts @@ -7,6 +7,7 @@ import { assert } from "chai"; +import { GraphError } from "../../src"; import { GraphErrorHandler } from "../../src/GraphErrorHandler"; describe("GraphErrorHandler.ts", () => { @@ -41,6 +42,7 @@ describe("GraphErrorHandler.ts", () => { it("Should construct error for error response without innerError property", () => { const gError = GraphErrorHandler["constructErrorFromResponse"](error, statusCode); + assert.isTrue(gError instanceof GraphError); assert.equal(gError.statusCode, statusCode); assert.equal(gError.requestId, null); }); @@ -50,6 +52,7 @@ describe("GraphErrorHandler.ts", () => { "request-id": "some random id", }; const gError = GraphErrorHandler["constructErrorFromResponse"](error, statusCode); + assert.isTrue(gError instanceof GraphError); assert.equal(gError.statusCode, statusCode); assert.equal(gError.requestId, "some random id"); }); @@ -62,6 +65,7 @@ describe("GraphErrorHandler.ts", () => { date, }; const gError = GraphErrorHandler["constructErrorFromResponse"](error, statusCode); + assert.isTrue(gError instanceof GraphError); assert.equal(gError.statusCode, statusCode); assert.equal(gError.requestId, "some random id"); assert.equal(gError.date.toUTCString(), date.toUTCString()); @@ -81,6 +85,7 @@ describe("GraphErrorHandler.ts", () => { }, }; const gError = await GraphErrorHandler.getError(errorResponse); + assert.isTrue(gError instanceof GraphError); assert.equal(gError.requestId, "some random id"); assert.equal(gError.code, "500"); assert.equal(gError.message, "Internal Server Error"); @@ -90,6 +95,7 @@ describe("GraphErrorHandler.ts", () => { const error = new Error("Some Error"); error.name = "InvalidError"; const gError = await GraphErrorHandler.getError(error); + assert.isTrue(gError instanceof GraphError); assert.equal(gError.requestId, null); assert.equal(gError.message, "Some Error"); assert.equal(gError.code, "InvalidError"); @@ -97,6 +103,7 @@ describe("GraphErrorHandler.ts", () => { it("Should construct some default error", async () => { const gError = await GraphErrorHandler.getError(); + assert.isTrue(gError instanceof GraphError); assert.equal(gError.message, ""); assert.equal(gError.statusCode, -1); assert.equal(gError.code, null); diff --git a/src/GraphError.ts b/src/GraphError.ts index d3433e879..b9b901c24 100644 --- a/src/GraphError.ts +++ b/src/GraphError.ts @@ -57,6 +57,8 @@ export class GraphError extends Error { */ public constructor(statusCode: number = -1, message?: string, baseError?: Error) { super(message || (baseError && baseError.message)); + // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, GraphError.prototype); this.statusCode = statusCode; this.code = null; this.requestId = null; diff --git a/src/Version.ts b/src/Version.ts index 64101ee23..13e0e9e67 100644 --- a/src/Version.ts +++ b/src/Version.ts @@ -12,4 +12,4 @@ * @module Version */ -export const PACKAGE_VERSION = "2.1.0-Preview.2"; +export const PACKAGE_VERSION = "2.2.0-Preview.1"; From c1b957fb4c762f7d4f22dcc909252e519ef3be59 Mon Sep 17 00:00:00 2001 From: nikithauc Date: Wed, 18 Nov 2020 14:37:28 -0800 Subject: [PATCH 54/55] Checking if the request object url is a graph url (#357) * Checking if the request object url is a graph url * removing ununsed const * Update src/middleware/TelemetryHandler.ts Co-authored-by: Vincent Biret Co-authored-by: Vincent Biret --- spec/middleware/TelemetryHandler.ts | 37 +++++++++++++++++++++++- src/middleware/TelemetryHandler.ts | 45 ++++++++++++++--------------- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/spec/middleware/TelemetryHandler.ts b/spec/middleware/TelemetryHandler.ts index 101a9ef5d..254c6aae7 100644 --- a/spec/middleware/TelemetryHandler.ts +++ b/spec/middleware/TelemetryHandler.ts @@ -21,13 +21,14 @@ describe("TelemetryHandler.ts", () => { this.timeout(20 * 1000); const telemetryHandler = new TelemetryHandler(); const dummyHTTPHandler = new DummyHTTPMessageHandler(); + const uuid = "dummy_uuid"; + const sdkVersion = "dummy_version"; telemetryHandler.setNext(dummyHTTPHandler); const okayResponse = new Response("", { status: 200, statusText: "OK", }); it("Should not disturb client-request-id in the header", async () => { - const uuid = "dummy_uuid"; const context: Context = { request: GRAPH_BASE_URL, options: { @@ -102,6 +103,40 @@ describe("TelemetryHandler.ts", () => { assert.equal(context.options.headers["SdkVersion"], undefined); assert.equal(context.options.headers["setFeatureUsage"], undefined); }); + + it("Should not disturb client-request-id in the header when Request object is passed with Graph URL", async () => { + const request = new Request(GRAPH_BASE_URL); + const context: Context = { + request, + options: { + headers: { + "client-request-id": uuid, + SdkVersion: sdkVersion, + }, + }, + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.equal(context.options.headers["client-request-id"], uuid); + assert.equal(context.options.headers["SdkVersion"], sdkVersion); + }); + + it("Should delete Telemetry in the header when Request object is passed with non Graph URL", async () => { + const request = new Request("test_url"); + const context: Context = { + request, + options: { + headers: { + "client-request-id": uuid, + SdkVersion: "test_version", + }, + }, + }; + dummyHTTPHandler.setResponses([okayResponse]); + await telemetryHandler.execute(context); + assert.equal(context.options.headers["client-request-id"], undefined); + assert.equal(context.options.headers["SdkVersion"], undefined); + }); }); /* tslint:enable: no-string-literal */ }); diff --git a/src/middleware/TelemetryHandler.ts b/src/middleware/TelemetryHandler.ts index 75b8b77ca..1c60c2a01 100644 --- a/src/middleware/TelemetryHandler.ts +++ b/src/middleware/TelemetryHandler.ts @@ -66,30 +66,29 @@ export class TelemetryHandler implements Middleware { */ public async execute(context: Context): Promise { try { - if (typeof context.request === "string") { - if (isGraphURL(context.request)) { - // Add telemetry only if the request url is a Graph URL. - // Errors are reported as in issue #265 if headers are present when redirecting to a non Graph URL - let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER); - if (clientRequestId === null) { - clientRequestId = generateUUID(); - setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId); - } - let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`; - let options: TelemetryHandlerOptions; - if (context.middlewareControl instanceof MiddlewareControl) { - options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions; - } - if (options) { - const featureUsage: string = options.getFeatureUsage(); - sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`; - } - appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue); - } else { - // Remove telemetry headers if present during redirection. - delete context.options.headers[TelemetryHandler.CLIENT_REQUEST_ID_HEADER]; - delete context.options.headers[TelemetryHandler.SDK_VERSION_HEADER]; + const url = typeof context.request === "string" ? context.request : context.request.url; + if (isGraphURL(url)) { + // Add telemetry only if the request url is a Graph URL. + // Errors are reported as in issue #265 if headers are present when redirecting to a non Graph URL + let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER); + if (!clientRequestId) { + clientRequestId = generateUUID(); + setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId); } + let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`; + let options: TelemetryHandlerOptions; + if (context.middlewareControl instanceof MiddlewareControl) { + options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions; + } + if (options) { + const featureUsage: string = options.getFeatureUsage(); + sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`; + } + appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue); + } else { + // Remove telemetry headers if present during redirection. + delete context.options.headers[TelemetryHandler.CLIENT_REQUEST_ID_HEADER]; + delete context.options.headers[TelemetryHandler.SDK_VERSION_HEADER]; } return await this.nextMiddleware.execute(context); } catch (error) { From e6be3d628ff2e05748e43efc93fbd33e99f85e90 Mon Sep 17 00:00:00 2001 From: Nikitha Chettiar Date: Mon, 30 Nov 2020 11:04:22 -0800 Subject: [PATCH 55/55] update to version 2.2.0 --- package-lock.json | 2 +- package.json | 2 +- src/Version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09a299ceb..469cdb75e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.2.0-Preview.1", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f9b1dda8c..beca1dd76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/microsoft-graph-client", - "version": "2.2.0-Preview.1", + "version": "2.2.0", "description": "Microsoft Graph Client Library", "license": "MIT", "main": "lib/src/index.js", diff --git a/src/Version.ts b/src/Version.ts index 13e0e9e67..16fd3ea1d 100644 --- a/src/Version.ts +++ b/src/Version.ts @@ -12,4 +12,4 @@ * @module Version */ -export const PACKAGE_VERSION = "2.2.0-Preview.1"; +export const PACKAGE_VERSION = "2.2.0";