Skip to content

Commit 81f0213

Browse files
authored
Merge branch 'dev' into nikithauc/update-TS-4.1.3
2 parents b3e3164 + ca3d823 commit 81f0213

File tree

8 files changed

+267
-13
lines changed

8 files changed

+267
-13
lines changed

docs/content/Batching.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,81 @@ const serialBatching = async function(elem) {
6666
};
6767
```
6868

69+
### Download multiple profile photos with batching and preprocess these for rendering in a browser
70+
71+
You should convert the downloaded photos through batching to a Base64 representation if you want to render these in a browser.
72+
73+
```typescript
74+
b64toBlob = async (b64Data:any, contentType:string, sliceSize?:number):Promise<Blob> => {
75+
contentType = contentType || 'image/png';
76+
sliceSize = sliceSize || 512;
77+
78+
let byteCharacters:string = atob(b64Data);
79+
let byteArrays = [];
80+
81+
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
82+
let slice = byteCharacters.slice(offset, offset + sliceSize);
83+
84+
let byteNumbers = new Array(slice.length);
85+
for (let i = 0; i < slice.length; i++) {
86+
byteNumbers[i] = slice.charCodeAt(i);
87+
}
88+
89+
let byteArray = new Uint8Array(byteNumbers);
90+
byteArrays.push(byteArray);
91+
}
92+
93+
let blob = new Blob(byteArrays, {type: contentType});
94+
return blob;
95+
};
96+
97+
blobToBase64 = (blob: Blob): Promise<string> => {
98+
return new Promise((resolve, reject) => {
99+
const reader = new FileReader();
100+
reader.onerror = reject;
101+
reader.onload = _ => {
102+
resolve(reader.result as string);
103+
};
104+
reader.readAsDataURL(blob);
105+
});
106+
};
107+
108+
downloadPhotosBatching = async (client: Client) => {
109+
try {
110+
111+
112+
// create batch request steps for the users specified above
113+
const batchRequestSteps : BatchRequestStep[] = users.map((userId) => {
114+
const request : BatchRequestStep = {
115+
id: userId,
116+
request: new Request(`/users/${userId}/photo/$value`, {
117+
method: "GET",
118+
})
119+
};
120+
return request;
121+
})
122+
123+
// initiate the batchrequest and execute the operation
124+
const batchRequestContent = new BatchRequestContent(batchRequestSteps);
125+
const content = await batchRequestContent.getContent();
126+
const batchResponse = new BatchResponseContent(await client.api("/$batch").post(content));
127+
128+
// example on how to retrieve the base64 representation of the downloaded image for the first user
129+
const response = batchResponse.getResponseById(users[0]);
130+
if (response.ok) {
131+
var data = await response.text();
132+
const binToBlob = await this.b64toBlob((data),'img/jpg');
133+
134+
// you can associate the base64 output to an src attribute of an <img> HTML tag
135+
const base64Result = await this.blobToBase64(binToBlob);
136+
console.log(base64Result);
137+
}
138+
} catch (error) {
139+
console.error(error);
140+
}
141+
};
142+
```
143+
69144
### GET and POST contents from and to different workloads - Making parallel requests
70145

71146
```typescript

msgraph-sdk-javascript.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
page_type: sample
2+
products:
3+
- office-365
4+
- ms-graph
5+
languages:
6+
- javascript
7+
- typescript
8+
extensions:
9+
contentType: sdks
10+
technologies:
11+
- Microsoft Graph
12+
- Microsoft identity platform
13+
createdDate: '9/22/2016 2:44:49 PM'
14+
title: Microsoft Graph JavaScript Client Library
15+
description: Microsoft Graph client library for JavaScript

spec/core/Client.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import "isomorphic-fetch";
1010

1111
import { CustomAuthenticationProvider, TelemetryHandler } from "../../src";
1212
import { Client } from "../../src/Client";
13+
import { GraphClientError } from "../../src/GraphClientError";
1314
import { AuthProvider } from "../../src/IAuthProvider";
1415
import { ClientOptions } from "../../src/IClientOptions";
1516
import { Options } from "../../src/IOptions";
@@ -101,6 +102,48 @@ describe("Client.ts", () => {
101102
const response = await client.api("me").get();
102103
assert.equal(response, responseBody);
103104
});
105+
106+
it("Should throw error in case the access token is undefined", async () => {
107+
try {
108+
const options = {
109+
defaultVersion: "v1.0",
110+
debugLogging: true,
111+
authProvider: (done) => {
112+
done(null, getTokenFunction());
113+
},
114+
};
115+
116+
const getTokenFunction = (): string => {
117+
return undefined;
118+
};
119+
const client = Client.init(options);
120+
const res = await client.api("/test").get();
121+
} catch (error) {
122+
assert.isTrue(error instanceof GraphClientError);
123+
assert.isDefined(error.message);
124+
}
125+
});
126+
127+
it("Should throw error in case the access token is empty", async () => {
128+
const customError = { message: "Token is empty" };
129+
try {
130+
const options = {
131+
defaultVersion: "v1.0",
132+
debugLogging: true,
133+
authProvider: (done) => {
134+
done(customError, getTokenFunction());
135+
},
136+
};
137+
const getTokenFunction = (): string => {
138+
return "";
139+
};
140+
const client = Client.init(options);
141+
const res = await client.api("/test").get();
142+
} catch (error) {
143+
assert.isTrue(error instanceof GraphClientError);
144+
assert.equal(error.customError, customError);
145+
}
146+
});
104147
});
105148

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

spec/core/GraphClientError.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* -------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
4+
* See License in the project root for license information.
5+
* -------------------------------------------------------------------------------------------
6+
*/
7+
8+
import { assert } from "chai";
9+
10+
import { GraphClientError } from "../../src/GraphClientError";
11+
12+
describe("GraphClientError", () => {
13+
const message = "test";
14+
const name = "test_name";
15+
it("Should return GraphClientError error with message set", () => {
16+
const gError = new GraphClientError(message);
17+
assert.equal(gError.message, message);
18+
});
19+
20+
it("Should return GraphClientError when Error object is passed", () => {
21+
const errorParameter = new Error(message);
22+
errorParameter.name = name;
23+
const gError = GraphClientError.setGraphClientError(errorParameter);
24+
assert.equal(gError.message, message);
25+
assert.equal(gError.name, name);
26+
});
27+
28+
it("Should return GraphClientError when custom error object is passed", () => {
29+
const customErrorParameter = { errorName: name, errorMessage: message };
30+
const gError = GraphClientError.setGraphClientError(customErrorParameter);
31+
assert.isDefined(gError.customError);
32+
assert.equal(gError.customError, customErrorParameter);
33+
});
34+
});

src/CustomAuthenticationProvider.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* @module CustomAuthenticationProvider
1010
*/
1111

12+
import { GraphClientError } from "./GraphClientError";
1213
import { AuthenticationProvider } from "./IAuthenticationProvider";
1314
import { AuthProvider } from "./IAuthProvider";
1415

@@ -43,11 +44,18 @@ export class CustomAuthenticationProvider implements AuthenticationProvider {
4344
*/
4445
public async getAccessToken(): Promise<any> {
4546
return new Promise((resolve: (accessToken: string) => void, reject: (error: any) => void) => {
46-
this.provider((error: any, accessToken: string | null) => {
47+
this.provider(async (error: any, accessToken: string | null) => {
4748
if (accessToken) {
4849
resolve(accessToken);
4950
} else {
50-
reject(error);
51+
if (!error) {
52+
const invalidTokenMessage = "Access token is undefined or empty.\
53+
Please provide a valid token.\
54+
For more help - https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CustomAuthenticationProvider.md";
55+
error = new GraphClientError(invalidTokenMessage);
56+
}
57+
const err = await GraphClientError.setGraphClientError(error);
58+
reject(err);
5159
}
5260
});
5361
});

src/GraphClientError.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* -------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
4+
* See License in the project root for license information.
5+
* -------------------------------------------------------------------------------------------
6+
*/
7+
8+
/**
9+
* @module GraphClientError
10+
*/
11+
12+
/**
13+
* @class
14+
* Create GraphClientError object to handle client-side errors
15+
* encountered within the JavaScript Client SDK.
16+
* Whereas GraphError Class should be used to handle errors in the response from the Graph API.
17+
*/
18+
19+
export class GraphClientError extends Error {
20+
/**
21+
* @public
22+
* A custom error. This property should set be when the error is not of instanceOf Error/GraphClientError.
23+
* Example =
24+
* const client = MicrosoftGraph.Client.init({
25+
* defaultVersion: "v1.0",
26+
* authProvider: (done) => { done({TokenError:"AccessToken cannot be null"}, "<ACCESS_TOKEN>");
27+
* });
28+
*/
29+
public customError?: any;
30+
31+
/**
32+
* @public
33+
* @static
34+
* @async
35+
* To set the GraphClientError object
36+
* @param {any} - The error returned encountered by the Graph JavaScript Client SDK while processing request
37+
* @returns GraphClientError object set to the error passed
38+
*/
39+
public static setGraphClientError(error: any): GraphClientError {
40+
let graphClientError: GraphClientError;
41+
if (error instanceof Error) {
42+
graphClientError = error;
43+
} else {
44+
graphClientError = new GraphClientError();
45+
graphClientError.customError = error;
46+
}
47+
return graphClientError;
48+
}
49+
50+
/**
51+
* @public
52+
* @constructor
53+
* Creates an instance of GraphClientError
54+
* @param {string} message? - Error message
55+
* @returns An instance of GraphClientError
56+
*/
57+
public constructor(message?: string) {
58+
super(message);
59+
Object.setPrototypeOf(this, GraphClientError.prototype);
60+
}
61+
}

src/GraphErrorHandler.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@
1212
import { GraphError } from "./GraphError";
1313
import { GraphRequestCallback } from "./IGraphRequestCallback";
1414

15+
/**
16+
* @interface
17+
* Signature for the json represent of the error response from the Graph API
18+
* https://docs.microsoft.com/en-us/graph/errors
19+
* @property {[key: string] : string | number} - The Key value pair
20+
*/
21+
interface GraphAPIErrorResponse {
22+
error: {
23+
code: string;
24+
message: string;
25+
innerError: any;
26+
};
27+
}
28+
1529
/**
1630
* @class
1731
* Class for GraphErrorHandler
@@ -41,7 +55,7 @@ export class GraphErrorHandler {
4155
* @static
4256
* @async
4357
* Populates the GraphError instance from the Error returned by graph service
44-
* @param {any} error - The error returned by graph service or some native error
58+
* @param {GraphAPIErrorResponse} graphError - The error possibly returned by graph service or some native error
4559
* @param {number} statusCode - The status code of the response
4660
* @returns A promise that resolves to GraphError instance
4761
*
@@ -57,19 +71,17 @@ export class GraphErrorHandler {
5771
* }
5872
* }
5973
*/
60-
private static constructErrorFromResponse(error: any, statusCode: number): GraphError {
61-
error = error.error;
74+
private static constructErrorFromResponse(graphError: GraphAPIErrorResponse, statusCode: number): GraphError {
75+
const error = graphError.error;
6276
const gError = new GraphError(statusCode, error.message);
6377
gError.code = error.code;
6478
if (error.innerError !== undefined) {
6579
gError.requestId = error.innerError["request-id"];
6680
gError.date = new Date(error.innerError.date);
6781
}
68-
try {
69-
gError.body = JSON.stringify(error);
70-
} catch (error) {
71-
// tslint:disable-line: no-empty
72-
}
82+
83+
gError.body = JSON.stringify(error);
84+
7385
return gError;
7486
}
7587

@@ -78,6 +90,7 @@ export class GraphErrorHandler {
7890
* @static
7991
* @async
8092
* To get the GraphError object
93+
* Reference - https://docs.microsoft.com/en-us/graph/errors
8194
* @param {any} [error = null] - The error returned by graph service or some native error
8295
* @param {number} [statusCode = -1] - The status code of the response
8396
* @param {GraphRequestCallback} [callback] - The graph request callback function
@@ -87,10 +100,11 @@ export class GraphErrorHandler {
87100
let gError: GraphError;
88101
if (error && error.error) {
89102
gError = GraphErrorHandler.constructErrorFromResponse(error, statusCode);
90-
} else if (typeof Error !== "undefined" && error instanceof Error) {
103+
} else if (error instanceof Error) {
91104
gError = GraphErrorHandler.constructError(error, statusCode);
92105
} else {
93106
gError = new GraphError(statusCode);
107+
gError.body = error; // if a custom error is passed which is not instance of Error object or a graph API response
94108
}
95109
if (typeof callback === "function") {
96110
callback(gError, null);

src/GraphRequest.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/**
99
* @module GraphRequest
1010
*/
11-
11+
import { GraphClientError } from "./GraphClientError";
1212
import { GraphError } from "./GraphError";
1313
import { GraphErrorHandler } from "./GraphErrorHandler";
1414
import { oDataQueryNames, serializeContent, urlJoin } from "./GraphRequestUtil";
@@ -379,8 +379,12 @@ export class GraphRequest {
379379
const response: any = await GraphResponseHandler.getResponse(rawResponse, this._responseType, callback);
380380
return response;
381381
} catch (error) {
382+
if (error instanceof GraphClientError) {
383+
throw error;
384+
}
382385
let statusCode: number;
383-
if (typeof rawResponse !== "undefined") {
386+
387+
if (rawResponse) {
384388
statusCode = rawResponse.status;
385389
}
386390
const gError: GraphError = await GraphErrorHandler.getError(error, statusCode, callback);

0 commit comments

Comments
 (0)