UNPKG

21.6 kBJavaScriptView Raw
1"use strict";
2/**
3 * -------------------------------------------------------------------------------------------
4 * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
5 * See License in the project root for license information.
6 * -------------------------------------------------------------------------------------------
7 */
8Object.defineProperty(exports, "__esModule", { value: true });
9exports.BatchRequestContent = void 0;
10var tslib_1 = require("tslib");
11/**
12 * @module BatchRequestContent
13 */
14var RequestMethod_1 = require("../RequestMethod");
15/**
16 * @class
17 * Class for handling BatchRequestContent
18 */
19var BatchRequestContent = /** @class */ (function () {
20 /**
21 * @public
22 * @constructor
23 * Constructs a BatchRequestContent instance
24 * @param {BatchRequestStep[]} [requests] - Array of requests value
25 * @returns An instance of a BatchRequestContent
26 */
27 function BatchRequestContent(requests) {
28 this.requests = new Map();
29 if (typeof requests !== "undefined") {
30 var limit = BatchRequestContent.requestLimit;
31 if (requests.length > limit) {
32 var error = new Error("Maximum requests limit exceeded, Max allowed number of requests are ".concat(limit));
33 error.name = "Limit Exceeded Error";
34 throw error;
35 }
36 for (var _i = 0, requests_1 = requests; _i < requests_1.length; _i++) {
37 var req = requests_1[_i];
38 this.addRequest(req);
39 }
40 }
41 }
42 /**
43 * @private
44 * @static
45 * Validates the dependency chain of the requests
46 *
47 * Note:
48 * Individual requests can depend on other individual requests. Currently, requests can only depend on a single other request, and must follow one of these three patterns:
49 * 1. Parallel - no individual request states a dependency in the dependsOn property.
50 * 2. Serial - all individual requests depend on the previous individual request.
51 * 3. Same - all individual requests that state a dependency in the dependsOn property, state the same dependency.
52 * As JSON batching matures, these limitations will be removed.
53 * @see {@link https://developer.microsoft.com/en-us/graph/docs/concepts/known_issues#json-batching}
54 *
55 * @param {Map<string, BatchRequestStep>} requests - The map of requests.
56 * @returns The boolean indicating the validation status
57 */
58 BatchRequestContent.validateDependencies = function (requests) {
59 var isParallel = function (reqs) {
60 var iterator = reqs.entries();
61 var cur = iterator.next();
62 while (!cur.done) {
63 var curReq = cur.value[1];
64 if (curReq.dependsOn !== undefined && curReq.dependsOn.length > 0) {
65 return false;
66 }
67 cur = iterator.next();
68 }
69 return true;
70 };
71 var isSerial = function (reqs) {
72 var iterator = reqs.entries();
73 var cur = iterator.next();
74 var firstRequest = cur.value[1];
75 if (firstRequest.dependsOn !== undefined && firstRequest.dependsOn.length > 0) {
76 return false;
77 }
78 var prev = cur;
79 cur = iterator.next();
80 while (!cur.done) {
81 var curReq = cur.value[1];
82 if (curReq.dependsOn === undefined || curReq.dependsOn.length !== 1 || curReq.dependsOn[0] !== prev.value[1].id) {
83 return false;
84 }
85 prev = cur;
86 cur = iterator.next();
87 }
88 return true;
89 };
90 var isSame = function (reqs) {
91 var iterator = reqs.entries();
92 var cur = iterator.next();
93 var firstRequest = cur.value[1];
94 var dependencyId;
95 if (firstRequest.dependsOn === undefined || firstRequest.dependsOn.length === 0) {
96 dependencyId = firstRequest.id;
97 }
98 else {
99 if (firstRequest.dependsOn.length === 1) {
100 var fDependencyId = firstRequest.dependsOn[0];
101 if (fDependencyId !== firstRequest.id && reqs.has(fDependencyId)) {
102 dependencyId = fDependencyId;
103 }
104 else {
105 return false;
106 }
107 }
108 else {
109 return false;
110 }
111 }
112 cur = iterator.next();
113 while (!cur.done) {
114 var curReq = cur.value[1];
115 if ((curReq.dependsOn === undefined || curReq.dependsOn.length === 0) && dependencyId !== curReq.id) {
116 return false;
117 }
118 if (curReq.dependsOn !== undefined && curReq.dependsOn.length !== 0) {
119 if (curReq.dependsOn.length === 1 && (curReq.id === dependencyId || curReq.dependsOn[0] !== dependencyId)) {
120 return false;
121 }
122 if (curReq.dependsOn.length > 1) {
123 return false;
124 }
125 }
126 cur = iterator.next();
127 }
128 return true;
129 };
130 if (requests.size === 0) {
131 var error = new Error("Empty requests map, Please provide at least one request.");
132 error.name = "Empty Requests Error";
133 throw error;
134 }
135 return isParallel(requests) || isSerial(requests) || isSame(requests);
136 };
137 /**
138 * @private
139 * @static
140 * @async
141 * Converts Request Object instance to a JSON
142 * @param {IsomorphicRequest} request - The IsomorphicRequest Object instance
143 * @returns A promise that resolves to JSON representation of a request
144 */
145 BatchRequestContent.getRequestData = function (request) {
146 return tslib_1.__awaiter(this, void 0, void 0, function () {
147 var requestData, hasHttpRegex, headers, _a;
148 return tslib_1.__generator(this, function (_b) {
149 switch (_b.label) {
150 case 0:
151 requestData = {
152 url: "",
153 };
154 hasHttpRegex = new RegExp("^https?://");
155 // Stripping off hostname, port and url scheme
156 requestData.url = hasHttpRegex.test(request.url) ? "/" + request.url.split(/.*?\/\/.*?\//)[1] : request.url;
157 requestData.method = request.method;
158 headers = {};
159 request.headers.forEach(function (value, key) {
160 headers[key] = value;
161 });
162 if (Object.keys(headers).length) {
163 requestData.headers = headers;
164 }
165 if (!(request.method === RequestMethod_1.RequestMethod.PATCH || request.method === RequestMethod_1.RequestMethod.POST || request.method === RequestMethod_1.RequestMethod.PUT)) return [3 /*break*/, 2];
166 _a = requestData;
167 return [4 /*yield*/, BatchRequestContent.getRequestBody(request)];
168 case 1:
169 _a.body = _b.sent();
170 _b.label = 2;
171 case 2:
172 /**
173 * TODO: Check any other property needs to be used from the Request object and add them
174 */
175 return [2 /*return*/, requestData];
176 }
177 });
178 });
179 };
180 /**
181 * @private
182 * @static
183 * @async
184 * Gets the body of a Request object instance
185 * @param {IsomorphicRequest} request - The IsomorphicRequest object instance
186 * @returns The Promise that resolves to a body value of a Request
187 */
188 BatchRequestContent.getRequestBody = function (request) {
189 return tslib_1.__awaiter(this, void 0, void 0, function () {
190 var bodyParsed, body, cloneReq, e_1, blob_1, reader_1, buffer, e_2;
191 return tslib_1.__generator(this, function (_a) {
192 switch (_a.label) {
193 case 0:
194 bodyParsed = false;
195 _a.label = 1;
196 case 1:
197 _a.trys.push([1, 3, , 4]);
198 cloneReq = request.clone();
199 return [4 /*yield*/, cloneReq.json()];
200 case 2:
201 body = _a.sent();
202 bodyParsed = true;
203 return [3 /*break*/, 4];
204 case 3:
205 e_1 = _a.sent();
206 return [3 /*break*/, 4];
207 case 4:
208 if (!!bodyParsed) return [3 /*break*/, 12];
209 _a.label = 5;
210 case 5:
211 _a.trys.push([5, 11, , 12]);
212 if (!(typeof Blob !== "undefined")) return [3 /*break*/, 8];
213 return [4 /*yield*/, request.blob()];
214 case 6:
215 blob_1 = _a.sent();
216 reader_1 = new FileReader();
217 return [4 /*yield*/, new Promise(function (resolve) {
218 reader_1.addEventListener("load", function () {
219 var dataURL = reader_1.result;
220 /**
221 * Some valid dataURL schemes:
222 * 1. data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh
223 * 2. data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678
224 * 3. data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
225 * 4. data:image/png,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
226 * 5. data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
227 * @see Syntax {@link https://en.wikipedia.org/wiki/Data_URI_scheme} for more
228 */
229 var regex = new RegExp("^s*data:(.+?/.+?(;.+?=.+?)*)?(;base64)?,(.*)s*$");
230 var segments = regex.exec(dataURL);
231 resolve(segments[4]);
232 }, false);
233 reader_1.readAsDataURL(blob_1);
234 })];
235 case 7:
236 body = _a.sent();
237 return [3 /*break*/, 10];
238 case 8:
239 if (!(typeof Buffer !== "undefined")) return [3 /*break*/, 10];
240 return [4 /*yield*/, request.buffer()];
241 case 9:
242 buffer = _a.sent();
243 body = buffer.toString("base64");
244 _a.label = 10;
245 case 10:
246 bodyParsed = true;
247 return [3 /*break*/, 12];
248 case 11:
249 e_2 = _a.sent();
250 return [3 /*break*/, 12];
251 case 12: return [2 /*return*/, body];
252 }
253 });
254 });
255 };
256 /**
257 * @public
258 * Adds a request to the batch request content
259 * @param {BatchRequestStep} request - The request value
260 * @returns The id of the added request
261 */
262 BatchRequestContent.prototype.addRequest = function (request) {
263 var limit = BatchRequestContent.requestLimit;
264 if (request.id === "") {
265 var error = new Error("Id for a request is empty, Please provide an unique id");
266 error.name = "Empty Id For Request";
267 throw error;
268 }
269 if (this.requests.size === limit) {
270 var error = new Error("Maximum requests limit exceeded, Max allowed number of requests are ".concat(limit));
271 error.name = "Limit Exceeded Error";
272 throw error;
273 }
274 if (this.requests.has(request.id)) {
275 var error = new Error("Adding request with duplicate id ".concat(request.id, ", Make the id of the requests unique"));
276 error.name = "Duplicate RequestId Error";
277 throw error;
278 }
279 this.requests.set(request.id, request);
280 return request.id;
281 };
282 /**
283 * @public
284 * Removes request from the batch payload and its dependencies from all dependents
285 * @param {string} requestId - The id of a request that needs to be removed
286 * @returns The boolean indicating removed status
287 */
288 BatchRequestContent.prototype.removeRequest = function (requestId) {
289 var deleteStatus = this.requests.delete(requestId);
290 var iterator = this.requests.entries();
291 var cur = iterator.next();
292 /**
293 * Removing dependencies where this request is present as a dependency
294 */
295 while (!cur.done) {
296 var dependencies = cur.value[1].dependsOn;
297 if (typeof dependencies !== "undefined") {
298 var index = dependencies.indexOf(requestId);
299 if (index !== -1) {
300 dependencies.splice(index, 1);
301 }
302 if (dependencies.length === 0) {
303 delete cur.value[1].dependsOn;
304 }
305 }
306 cur = iterator.next();
307 }
308 return deleteStatus;
309 };
310 /**
311 * @public
312 * @async
313 * Serialize content from BatchRequestContent instance
314 * @returns The body content to make batch request
315 */
316 BatchRequestContent.prototype.getContent = function () {
317 return tslib_1.__awaiter(this, void 0, void 0, function () {
318 var requests, requestBody, iterator, cur, error, error, requestStep, batchRequestData, error;
319 return tslib_1.__generator(this, function (_a) {
320 switch (_a.label) {
321 case 0:
322 requests = [];
323 requestBody = {
324 requests: requests,
325 };
326 iterator = this.requests.entries();
327 cur = iterator.next();
328 if (cur.done) {
329 error = new Error("No requests added yet, Please add at least one request.");
330 error.name = "Empty Payload";
331 throw error;
332 }
333 if (!BatchRequestContent.validateDependencies(this.requests)) {
334 error = new Error("Invalid dependency found, Dependency should be:\n1. Parallel - no individual request states a dependency in the dependsOn property.\n2. Serial - all individual requests depend on the previous individual request.\n3. Same - all individual requests that state a dependency in the dependsOn property, state the same dependency.");
335 error.name = "Invalid Dependency";
336 throw error;
337 }
338 _a.label = 1;
339 case 1:
340 if (!!cur.done) return [3 /*break*/, 3];
341 requestStep = cur.value[1];
342 return [4 /*yield*/, BatchRequestContent.getRequestData(requestStep.request)];
343 case 2:
344 batchRequestData = (_a.sent());
345 /**
346 * @see{@https://tools.ietf.org/html/rfc7578#section-4.4}
347 * TODO- Setting/Defaulting of content-type header to the correct value
348 * @see {@link https://developer.microsoft.com/en-us/graph/docs/concepts/json_batching#request-format}
349 */
350 if (batchRequestData.body !== undefined && (batchRequestData.headers === undefined || batchRequestData.headers["content-type"] === undefined)) {
351 error = new Error("Content-type header is not mentioned for request #".concat(requestStep.id, ", For request having body, Content-type header should be mentioned"));
352 error.name = "Invalid Content-type header";
353 throw error;
354 }
355 batchRequestData.id = requestStep.id;
356 if (requestStep.dependsOn !== undefined && requestStep.dependsOn.length > 0) {
357 batchRequestData.dependsOn = requestStep.dependsOn;
358 }
359 requests.push(batchRequestData);
360 cur = iterator.next();
361 return [3 /*break*/, 1];
362 case 3:
363 requestBody.requests = requests;
364 return [2 /*return*/, requestBody];
365 }
366 });
367 });
368 };
369 /**
370 * @public
371 * Adds a dependency for a given dependent request
372 * @param {string} dependentId - The id of the dependent request
373 * @param {string} [dependencyId] - The id of the dependency request, if not specified the preceding request will be considered as a dependency
374 * @returns Nothing
375 */
376 BatchRequestContent.prototype.addDependency = function (dependentId, dependencyId) {
377 if (!this.requests.has(dependentId)) {
378 var error = new Error("Dependent ".concat(dependentId, " does not exists, Please check the id"));
379 error.name = "Invalid Dependent";
380 throw error;
381 }
382 if (typeof dependencyId !== "undefined" && !this.requests.has(dependencyId)) {
383 var error = new Error("Dependency ".concat(dependencyId, " does not exists, Please check the id"));
384 error.name = "Invalid Dependency";
385 throw error;
386 }
387 if (typeof dependencyId !== "undefined") {
388 var dependent = this.requests.get(dependentId);
389 if (dependent.dependsOn === undefined) {
390 dependent.dependsOn = [];
391 }
392 if (dependent.dependsOn.indexOf(dependencyId) !== -1) {
393 var error = new Error("Dependency ".concat(dependencyId, " is already added for the request ").concat(dependentId));
394 error.name = "Duplicate Dependency";
395 throw error;
396 }
397 dependent.dependsOn.push(dependencyId);
398 }
399 else {
400 var iterator = this.requests.entries();
401 var prev = void 0;
402 var cur = iterator.next();
403 while (!cur.done && cur.value[1].id !== dependentId) {
404 prev = cur;
405 cur = iterator.next();
406 }
407 if (typeof prev !== "undefined") {
408 var dId = prev.value[0];
409 if (cur.value[1].dependsOn === undefined) {
410 cur.value[1].dependsOn = [];
411 }
412 if (cur.value[1].dependsOn.indexOf(dId) !== -1) {
413 var error = new Error("Dependency ".concat(dId, " is already added for the request ").concat(dependentId));
414 error.name = "Duplicate Dependency";
415 throw error;
416 }
417 cur.value[1].dependsOn.push(dId);
418 }
419 else {
420 var error = new Error("Can't add dependency ".concat(dependencyId, ", There is only a dependent request in the batch"));
421 error.name = "Invalid Dependency Addition";
422 throw error;
423 }
424 }
425 };
426 /**
427 * @public
428 * Removes a dependency for a given dependent request id
429 * @param {string} dependentId - The id of the dependent request
430 * @param {string} [dependencyId] - The id of the dependency request, if not specified will remove all the dependencies of that request
431 * @returns The boolean indicating removed status
432 */
433 BatchRequestContent.prototype.removeDependency = function (dependentId, dependencyId) {
434 var request = this.requests.get(dependentId);
435 if (typeof request === "undefined" || request.dependsOn === undefined || request.dependsOn.length === 0) {
436 return false;
437 }
438 if (typeof dependencyId !== "undefined") {
439 var index = request.dependsOn.indexOf(dependencyId);
440 if (index === -1) {
441 return false;
442 }
443 request.dependsOn.splice(index, 1);
444 return true;
445 }
446 else {
447 delete request.dependsOn;
448 return true;
449 }
450 };
451 /**
452 * @private
453 * @static
454 * Limit for number of requests {@link - https://developer.microsoft.com/en-us/graph/docs/concepts/known_issues#json-batching}
455 */
456 BatchRequestContent.requestLimit = 20;
457 return BatchRequestContent;
458}());
459exports.BatchRequestContent = BatchRequestContent;
460//# sourceMappingURL=BatchRequestContent.js.map
\No newline at end of file