Skip to content

Commit 9da92e1

Browse files
committed
Designed ChaosHandler
1 parent ea5f4ab commit 9da92e1

File tree

7 files changed

+824
-1
lines changed

7 files changed

+824
-1
lines changed

docs/ChaosHandlerSamples.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Testing Handler
2+
3+
### How to include
4+
5+
> Uses [Custom Middleware Chain](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CustomMiddlewareChain.md), it's not included in default middleware chain
6+
7+
### Modes in Testing Handler
8+
9+
- 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).
10+
- Random mode - We get a random Response code from a set of response code defined for each method.
11+
12+
### Samples
13+
14+
```js
15+
require("isomorphic-fetch");
16+
const MicrosoftGraph = require("../../lib/src/index.js");
17+
const secrets = require("./secrets");
18+
const fs = require("fs");
19+
// Initialising the client
20+
const client = MicrosoftGraph.Client.init({
21+
defaultVersion: "v1.0",
22+
debugLogging: true,
23+
authProvider: (done) => {
24+
done(null, secrets.accessToken);
25+
},
26+
});
27+
28+
// Declaring the Map, containing response codes for the urls
29+
const manualMap = new Map([["/me/messages/.*", new Map([["GET", 429], ["PATCH", 429]])], ["/me", new Map([["POST", 502]])]]);
30+
31+
// Declaring the chaosHandler and passing the map (if using map, we have to put default strategy as MANUAL)
32+
const chaosHandler = new MicrosoftGraph.ChaosHandler(new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.MANUAL), manualMap);
33+
34+
// Modifying the default middleware chain to add chaos handler, just before httpMessageHandler, otherwise there can be a problem
35+
let arr = client.getMiddlewareChain();
36+
arr.splice(arr.length - 1, 0, chaosHandler);
37+
client.setMiddlewareChain(arr);
38+
39+
// This request would use the Map (Manual mode)
40+
const mail = {
41+
subject: "Chaos Handler Samples",
42+
toRecipients: [
43+
{
44+
emailAddress: {
45+
address: "admin@M365x003297.OnMicrosoft.com",
46+
},
47+
},
48+
],
49+
body: {
50+
content: "<h1>Testing Handler Samples Sample</h1><br>https://github.com/microsoftgraph/msgraph-sdk-javascript",
51+
contentType: "html",
52+
},
53+
};
54+
client
55+
.api("/users/me/sendMail")
56+
.post({
57+
message: mail,
58+
})
59+
.then((res) => {
60+
console.log(res, "This is for sendMail");
61+
})
62+
.catch((err) => {
63+
console.log(err, "This is for sendMail in error case");
64+
});
65+
66+
// OverRiding to Random mode, providing the chaos percentage as 60(percentage times the error would be generated from handler)
67+
client
68+
.api("/me")
69+
.middlewareOptions([new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.RANDOM, undefined, "I generated the error", 60)])
70+
.get()
71+
.then((res) => {
72+
console.log(res);
73+
})
74+
.catch((err) => {
75+
console.log(err);
76+
});
77+
78+
// This request is passed to the graph and gets a response from the graph, as no entry for /me GET request in the Map
79+
client
80+
.api("/me")
81+
.get()
82+
.then((res) => {
83+
console.log("Found", res, "users");
84+
})
85+
.catch((err) => {
86+
console.log(err, "!!!!!!!!!");
87+
});
88+
89+
// Using Manual Map with regex matching
90+
client
91+
.api("/me/messages/hjdlfslod-fdssdkjfs-6zdkmghs-sadhsu2")
92+
.header("content-type", "application/json")
93+
.update({
94+
birthday: "1908-12-22T00:00:00Z",
95+
})
96+
.then((res) => {
97+
console.log("This is regex matching... Updated Bday");
98+
})
99+
.catch((err) => {
100+
console.log(err, "matched");
101+
});
102+
```

docs/content/Batching.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const serialBatching = async function(elem) {
5656
let batchResponseContent = new MicrosoftGraph.BatchResponseContent(response);
5757

5858
//Getting response by id
59-
console.log(batchResponse.getResponseById(downloadId));
59+
console.log(batchResponseContent.getResponseById(downloadId));
6060

6161
//Getting all the responses
6262
console.log(batchResponseContent.getResponses());

spec/middleware/ChaosHandler.ts

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import { assert } from "chai";
2+
3+
import { Context } from "../../src/IContext";
4+
import { ChaosHandler } from "../../src/middleware/ChaosHandler";
5+
import { MiddlewareControl } from "../../src/middleware/MiddlewareControl";
6+
import { ChaosHandlerOptions } from "../../src/middleware/options/ChaosHandlerOptions";
7+
import { ChaosStrategy } from "../../src/middleware/options/ChaosStrategy";
8+
import { RequestMethod } from "../../src/RequestMethod";
9+
import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler";
10+
11+
const chaosHandlerOptions = new ChaosHandlerOptions();
12+
const chaosHandler = new ChaosHandler();
13+
14+
describe("ChaosHandler.ts", () => {
15+
/* tslint:disable: no-string-literal */
16+
describe("constructor", () => {
17+
it("Should create an instance with given options", () => {
18+
const handler = new ChaosHandler(chaosHandlerOptions);
19+
assert.isDefined(handler["options"]);
20+
});
21+
22+
it("Should create an instance with default set of options", () => {
23+
const handler = new ChaosHandler();
24+
assert.isDefined(handler["options"]);
25+
});
26+
});
27+
28+
describe("createResponseHeaders", () => {
29+
it("Should have request-id for every random statusCode", () => {
30+
const responseHeader = chaosHandler["createResponseHeaders"](204, "xxxxxxxxxxxxxxxx", new Date().toString());
31+
assert.isDefined(responseHeader.get("request-id"));
32+
});
33+
34+
it("Should have retry-after for 429 case", () => {
35+
const responseHeader = chaosHandler["createResponseHeaders"](429, "xxxxxxxxxxxxxxxx", new Date().toString());
36+
assert.isDefined(responseHeader.get("retry-after"));
37+
});
38+
});
39+
40+
describe("createResponseBody", () => {
41+
it("Should return error in response body for error scenarios", () => {
42+
const responseBody = chaosHandler["createResponseBody"](404, "Not Found", "xxxxxxxxxxxxxx", new Date().toString());
43+
assert.isDefined(responseBody["error"]);
44+
});
45+
46+
it("Should return empty response body for success scenarios", () => {
47+
const responseBody = chaosHandler["createResponseBody"](200, "Not Found", "xxxxxxxxxxxxxx", new Date().toString());
48+
assert.equal(Object.keys(responseBody).length, 0);
49+
});
50+
});
51+
52+
describe("createResponse", () => {
53+
const cxt: Context = {
54+
request: "https://graph.microsoft.com/v1.0/me",
55+
options: {
56+
method: "GET",
57+
},
58+
};
59+
60+
it("Should return a valid response object for MANUAL case", () => {
61+
chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404), cxt);
62+
assert.isDefined(cxt.response);
63+
});
64+
65+
it("Should return a valid response object for RANDOM case", () => {
66+
chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.RANDOM), cxt);
67+
assert.isDefined(cxt.response);
68+
});
69+
});
70+
71+
describe("sendRequest", async () => {
72+
const cxt: Context = {
73+
request: "https://graph.microsoft.com/v1.0/me",
74+
options: {
75+
method: "GET",
76+
},
77+
};
78+
79+
const manualMap: Map<string, Map<string, number>> = new Map([["/me", new Map([["GET", 500]])]]);
80+
const tempManualOptions: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL);
81+
const tempChaosHandler = new ChaosHandler(tempManualOptions, manualMap);
82+
83+
const dummyHTTPHandler = new DummyHTTPMessageHandler();
84+
const handler = new ChaosHandler();
85+
handler.setNext(dummyHTTPHandler);
86+
87+
it("Should return a response after creating it", async () => {
88+
tempChaosHandler["sendRequest"](tempManualOptions, cxt);
89+
assert.isDefined(cxt.response);
90+
});
91+
92+
it("Should send the request to the graph", async () => {
93+
handler["sendRequest"](new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100), cxt);
94+
assert.isDefined(cxt.response);
95+
});
96+
});
97+
98+
describe("getRandomStatusCode", () => {
99+
it("Should return a status code for GET method", () => {
100+
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.GET));
101+
});
102+
103+
it("Should return a status code for POST method", () => {
104+
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.POST));
105+
});
106+
107+
it("Should return a status code for PUT method", () => {
108+
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.PUT));
109+
});
110+
111+
it("Should return a status code for PATCH method", () => {
112+
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.PATCH));
113+
});
114+
115+
it("Should return a status code for DELETE method", () => {
116+
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.DELETE));
117+
});
118+
});
119+
120+
describe("getRelativeURL", () => {
121+
it("Should return a relative URL for the complete URL", () => {
122+
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me"), "/me");
123+
});
124+
125+
it("Should return a relative URL for the complete URL with filter", () => {
126+
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me/messages?filter=emailAddress eq 'jon@contoso.com'"), "/me/messages");
127+
});
128+
129+
it("Should return a relative URL for the complete URL with ids", () => {
130+
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me/messages/q1abcxx-xxxxxx-xxxxabc"), "/me/messages/q1abcxx-xxxxxx-xxxxabc");
131+
});
132+
133+
it("Should return a relative URL for the complete URL in case of beta", () => {
134+
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/beta/me/messages"), "/me/messages");
135+
});
136+
});
137+
138+
describe("setStatusCode", () => {
139+
const manualMap: Map<string, Map<string, number>> = new Map([["/me/messages/.*", new Map([["GET", 500], ["PATCH", 201]])], ["/me", new Map([["GET", 500], ["PATCH", 201]])]]);
140+
const tempManualOptions: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL);
141+
const tempManualOptionsRegex: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL);
142+
const tempChaosHandlerManual = new ChaosHandler(tempManualOptions, manualMap);
143+
const tempChaosHandlerManualRegex = new ChaosHandler(tempManualOptionsRegex, manualMap);
144+
145+
it("Should set a statusCode for MANUAL mode", () => {
146+
const tempOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404);
147+
chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.GET);
148+
assert.isDefined(tempOptions.statusCode);
149+
});
150+
151+
it("Should set a statusCode for RANDOM mode", () => {
152+
const tempOptions = new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100);
153+
chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.POST);
154+
assert.isDefined(tempOptions.statusCode);
155+
});
156+
157+
it("Should set a statusCode for MANUAL mode with manualMap", () => {
158+
tempChaosHandlerManual["setStatusCode"](tempManualOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.PATCH);
159+
assert.equal(tempManualOptions.statusCode, 201);
160+
});
161+
162+
it("Should set a statusCode for MANUAL mode with manualMap matching regex", () => {
163+
tempChaosHandlerManualRegex["setStatusCode"](tempManualOptionsRegex, "https://graph.microsoft.com/v1.0/me/messages/abc123-xxxxx-xxxxx", RequestMethod.GET);
164+
assert.equal(tempManualOptionsRegex.statusCode, 500);
165+
});
166+
});
167+
168+
describe("getOptions", () => {
169+
it("Should return the options in the context object", () => {
170+
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 405);
171+
const cxt: Context = {
172+
request: "url",
173+
middlewareControl: new MiddlewareControl([options]),
174+
};
175+
const o = chaosHandler["getOptions"](cxt);
176+
assert.equal(o.chaosStrategy, ChaosStrategy.MANUAL);
177+
assert.equal(o.statusCode, 405);
178+
});
179+
180+
it("Should return the default set of options with RANDOM in the middleware", () => {
181+
const cxt: Context = {
182+
request: "url",
183+
};
184+
const o = chaosHandler["getOptions"](cxt);
185+
assert.equal(o.chaosStrategy, ChaosStrategy.RANDOM);
186+
assert.equal(o.statusCode, undefined);
187+
});
188+
189+
it("Should return the default set of options with DEFAULT in the middleware", () => {
190+
const tempChaosHandler = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.MANUAL));
191+
const cxt: Context = {
192+
request: "url",
193+
};
194+
const o = tempChaosHandler["getOptions"](cxt);
195+
assert.equal(o.chaosStrategy, ChaosStrategy.MANUAL);
196+
assert.equal(o.statusCode, undefined);
197+
});
198+
});
199+
200+
describe("execute", async () => {
201+
const manualMap: Map<string, Map<string, number>> = new Map([["/me", new Map([["GET", 500], ["PATCH", 201]])]]);
202+
const dummyHTTPHandler = new DummyHTTPMessageHandler();
203+
const tempChaosHandlerDefault = new ChaosHandler(new ChaosHandlerOptions());
204+
const tempChaosHandlerRandom = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.RANDOM));
205+
const tempChaosHandlerManual = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.MANUAL), manualMap);
206+
tempChaosHandlerDefault.setNext(dummyHTTPHandler);
207+
tempChaosHandlerRandom.setNext(dummyHTTPHandler);
208+
tempChaosHandlerManual.setNext(dummyHTTPHandler);
209+
210+
it("Should return response for Default Case", async () => {
211+
const options = new ChaosHandlerOptions(ChaosStrategy.RANDOM);
212+
const cxt: Context = {
213+
request: "https://graph.microsoft.com/v1.0/me",
214+
options: {
215+
method: "GET",
216+
},
217+
middlewareControl: new MiddlewareControl([options]),
218+
};
219+
assert.isDefined(tempChaosHandlerDefault["execute"](cxt));
220+
});
221+
222+
it("Should return response for Random case", async () => {
223+
const cxt: Context = {
224+
request: "https://graph.microsoft.com/v1.0/me",
225+
options: {
226+
method: "GET",
227+
},
228+
};
229+
assert.isDefined(tempChaosHandlerRandom["execute"](cxt));
230+
});
231+
232+
it("Should return response for Manual Global case", async () => {
233+
const cxt: Context = {
234+
request: "https://graph.microsoft.com/v1.0/me",
235+
options: {
236+
method: "GET",
237+
},
238+
};
239+
assert.isDefined(tempChaosHandlerManual["execute"](cxt));
240+
});
241+
242+
it("Should return response for Manual Request Level case", async () => {
243+
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200);
244+
const cxt: Context = {
245+
request: "https://graph.microsoft.com/v1.0/me",
246+
options: {
247+
method: "GET",
248+
},
249+
middlewareControl: new MiddlewareControl([options]),
250+
};
251+
assert.isDefined(tempChaosHandlerManual["execute"](cxt));
252+
});
253+
});
254+
});

0 commit comments

Comments
 (0)