Skip to content

Commit de0c225

Browse files
upd: make batch request when the url length is bigger than the limit. 1st part of a batch request implementation
1 parent e05de49 commit de0c225

File tree

12 files changed

+568
-228
lines changed

12 files changed

+568
-228
lines changed

DynamicsWebApi.njsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
<Compile Include="lib\helpers\ErrorHelper.js">
4242
<SubType>Code</SubType>
4343
</Compile>
44+
<Compile Include="lib\requests\helpers\parseResponse.js">
45+
<SubType>Code</SubType>
46+
</Compile>
4447
<Compile Include="lib\utilities\buildFunctionParameters.js">
4548
<SubType>Code</SubType>
4649
</Compile>

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ var AuthenticationContext = require('adal-node').AuthenticationContext;
8686
//and stored in app settings file or in global variables
8787

8888
//OAuth Token Endpoint
89-
var authorityUrl = 'https://login.windows.net/00000000-0000-0000-0000-000000000011/oauth2/token';
89+
var authorityUrl = 'https://login.microsoftonline.com/00000000-0000-0000-0000-000000000011/oauth2/token';
9090
//CRM Organization URL
9191
var resource = 'https://myorg.crm.dynamics.com';
9292
//Dynamics 365 Client Id when registered in Azure

dist/dynamics-web-api-callbacks.js

Lines changed: 121 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ return /******/ (function(modules) { // webpackBootstrap
7474
/******/ __webpack_require__.p = "";
7575
/******/
7676
/******/ // Load entry module and return exports
77-
/******/ return __webpack_require__(__webpack_require__.s = 13);
77+
/******/ return __webpack_require__(__webpack_require__.s = 14);
7878
/******/ })
7979
/************************************************************************/
8080
/******/ ([
@@ -320,6 +320,29 @@ String.prototype.startsWith = function (searchString, position) {
320320
/***/ (function(module, exports, __webpack_require__) {
321321

322322
var DWA = __webpack_require__(0);
323+
//var RequestConverter = require('../utilities/RequestConverter');
324+
325+
//https://stackoverflow.com/a/8809472
326+
function generateUUID() { // Public Domain/MIT
327+
var d = new Date().getTime();
328+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
329+
d += performance.now(); //use high-precision timer if available
330+
}
331+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
332+
var r = (d + Math.random() * 16) % 16 | 0;
333+
d = Math.floor(d / 16);
334+
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
335+
});
336+
}
337+
338+
function setStandardHeaders(additionalHeaders) {
339+
additionalHeaders["Accept"] = "application/json";
340+
additionalHeaders["OData-MaxVersion"] = "4.0";
341+
additionalHeaders["OData-Version"] = "4.0";
342+
additionalHeaders['Content-Type'] = 'application/json; charset=utf-8';
343+
344+
return additionalHeaders;
345+
}
323346

324347
/**
325348
* Sends a request to given URL with given parameters
@@ -334,21 +357,21 @@ var DWA = __webpack_require__(0);
334357
* @returns {Promise}
335358
*/
336359
module.exports = function sendRequest(method, uri, config, data, additionalHeaders, successCallback, errorCallback) {
337-
if (config.impersonate && (!additionalHeaders || (additionalHeaders && !additionalHeaders["MSCRMCallerID"]))) {
338-
if (!additionalHeaders) {
339-
additionalHeaders = {};
340-
}
341-
additionalHeaders['MSCRMCallerID'] = config.impersonate;
360+
361+
if (!additionalHeaders) {
362+
additionalHeaders = {};
342363
}
343364

365+
additionalHeaders = setStandardHeaders(additionalHeaders);
366+
344367
var stringifiedData;
345368
if (data) {
346369
stringifiedData = JSON.stringify(data, function (key, value) {
347370
/// <param name="key" type="String">Description</param>
348371
if (key.endsWith("@odata.bind")) {
349372
if (typeof value === "string") {
350373
//remove brackets in guid
351-
if (/\(\{[\w\d-]+\}\)/g.test(value)){
374+
if (/\(\{[\w\d-]+\}\)/g.test(value)) {
352375
value = value.replace(/(.+)\(\{([\w\d-]+)\}\)/g, '$1($2)');
353376
}
354377
//add full web api url if it's not set
@@ -362,9 +385,38 @@ module.exports = function sendRequest(method, uri, config, data, additionalHeade
362385
});
363386
}
364387

388+
//if the URL contains more characters than max possible limit, convert the request to a batch request
389+
if (uri.length > 2083) {
390+
var batchBoundary = 'dwa_batch_' + generateUUID();
391+
392+
var batchBody = [];
393+
batchBody.push('--' + batchBoundary);
394+
batchBody.push('Content-Type: application/http');
395+
batchBody.push('Content-Transfer-Encoding: binary\n');
396+
batchBody.push(method + ' ' + config.webApiUrl + uri + ' HTTP/1.1');
397+
398+
for (var key in additionalHeaders) {
399+
batchBody.push(key + ': ' + additionalHeaders[key]);
400+
delete additionalHeaders[key];
401+
}
402+
403+
batchBody.push('\n--' + batchBoundary + '--');
404+
405+
stringifiedData = batchBody.join('\n');
406+
407+
additionalHeaders = setStandardHeaders(additionalHeaders);
408+
additionalHeaders['Content-Type'] = 'multipart/mixed;boundary=' + batchBoundary;
409+
uri = '$batch';
410+
method = 'POST';
411+
}
412+
413+
if (config.impersonate && !additionalHeaders['MSCRMCallerID']) {
414+
additionalHeaders['MSCRMCallerID'] = config.impersonate;
415+
}
416+
365417
var executeRequest;
366418
if (typeof XMLHttpRequest !== 'undefined') {
367-
executeRequest = __webpack_require__(8);
419+
executeRequest = __webpack_require__(9);
368420
}
369421

370422

@@ -373,14 +425,14 @@ module.exports = function sendRequest(method, uri, config, data, additionalHeade
373425
if (!additionalHeaders) {
374426
additionalHeaders = {};
375427
}
376-
additionalHeaders['Authorization'] = "Bearer " + token.accessToken;
428+
additionalHeaders['Authorization'] = 'Bearer ' + token.accessToken;
377429
}
378430

379431
executeRequest(method, config.webApiUrl + uri, stringifiedData, additionalHeaders, successCallback, errorCallback);
380432
};
381433

382434
//call a token refresh callback only if it is set and there is no "Authorization" header set yet
383-
if (config.onTokenRefresh && (!additionalHeaders || (additionalHeaders && !additionalHeaders["Authorization"]))) {
435+
if (config.onTokenRefresh && (!additionalHeaders || (additionalHeaders && !additionalHeaders['Authorization']))) {
384436
config.onTokenRefresh(sendInternalRequest);
385437
}
386438
else {
@@ -394,7 +446,7 @@ module.exports = function sendRequest(method, uri, config, data, additionalHeade
394446

395447
var DWA = __webpack_require__(0);
396448
var ErrorHelper = __webpack_require__(1);
397-
var buildPreferHeader = __webpack_require__(10);
449+
var buildPreferHeader = __webpack_require__(11);
398450

399451
/**
400452
* @typedef {Object} ConvertedRequestOptions
@@ -588,7 +640,7 @@ var Utility = {
588640
* @param {Object} [parameters] - Function's input parameters. Example: { param1: "test", param2: 3 }.
589641
* @returns {string}
590642
*/
591-
buildFunctionParameters: __webpack_require__(9),
643+
buildFunctionParameters: __webpack_require__(10),
592644

593645
/**
594646
* Parses a paging cookie returned in response
@@ -597,15 +649,15 @@ var Utility = {
597649
* @param {number} currentPageNumber - A current page number. Fix empty paging-cookie for complex fetch xmls.
598650
* @returns {{cookie: "", number: 0, next: 1}}
599651
*/
600-
getFetchXmlPagingCookie: __webpack_require__(12),
652+
getFetchXmlPagingCookie: __webpack_require__(13),
601653

602654
/**
603655
* Converts a response to a reference object
604656
*
605657
* @param {Object} responseData - Response object
606658
* @returns {ReferenceObject}
607659
*/
608-
convertToReferenceObject: __webpack_require__(11)
660+
convertToReferenceObject: __webpack_require__(12)
609661
}
610662

611663
module.exports = Utility;
@@ -636,6 +688,46 @@ module.exports = function dateReviver(key, value) {
636688

637689
/***/ }),
638690
/* 7 */
691+
/***/ (function(module, exports, __webpack_require__) {
692+
693+
var dateReviver = __webpack_require__(6);
694+
695+
function parseBatchResponse(response) {
696+
// Not the same delimiter in the response as we specify ourselves in the request,
697+
// so we have to extract it.
698+
var delimiter = response.substr(0, response.indexOf('\r\n'));
699+
var parts = response.split(delimiter);
700+
// The first part will always be an empty string. Just remove it.
701+
parts.shift();
702+
// The last part will be the "--". Just remove it.
703+
parts.pop();
704+
705+
var result = [];
706+
for (var i = 0; i < parts.length; i++) {
707+
var part = parts[i];
708+
var p = part.substring(part.indexOf("{"), part.lastIndexOf("}") + 1);
709+
result.push(JSON.parse(p, dateReviver));
710+
}
711+
return result;
712+
}
713+
714+
/**
715+
*
716+
* @param {string} response
717+
*/
718+
module.exports = function parseResponse(response) {
719+
var responseData = null;
720+
if (response.length) {
721+
responseData = response.indexOf('--batchresponse_') > -1
722+
? responseData = parseBatchResponse(response)[0]
723+
: responseData = JSON.parse(response, dateReviver);
724+
}
725+
726+
return responseData
727+
}
728+
729+
/***/ }),
730+
/* 8 */
639731
/***/ (function(module, exports) {
640732

641733
module.exports = function parseResponseHeaders(headerStr) {
@@ -655,12 +747,11 @@ module.exports = function parseResponseHeaders(headerStr) {
655747
};
656748

657749
/***/ }),
658-
/* 8 */
750+
/* 9 */
659751
/***/ (function(module, exports, __webpack_require__) {
660752

661-
662-
var dateReviver = __webpack_require__(6);
663-
var parseResponseHeaders = __webpack_require__(7);
753+
var parseResponse = __webpack_require__(7);
754+
var parseResponseHeaders = __webpack_require__(8);
664755

665756
/**
666757
* Sends a request to given URL with given parameters
@@ -675,16 +766,14 @@ var parseResponseHeaders = __webpack_require__(7);
675766
var xhrRequest = function (method, uri, data, additionalHeaders, successCallback, errorCallback) {
676767
var request = new XMLHttpRequest();
677768
request.open(method, uri, true);
678-
request.setRequestHeader("OData-MaxVersion", "4.0");
679-
request.setRequestHeader("OData-Version", "4.0");
680-
request.setRequestHeader("Accept", "application/json");
681-
request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
769+
//request.setRequestHeader("OData-MaxVersion", "4.0");
770+
//request.setRequestHeader("OData-Version", "4.0");
771+
//request.setRequestHeader("Accept", "application/json");
772+
//request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
682773

683774
//set additional headers
684-
if (additionalHeaders != null) {
685-
for (var key in additionalHeaders) {
686-
request.setRequestHeader(key, additionalHeaders[key]);
687-
}
775+
for (var key in additionalHeaders) {
776+
request.setRequestHeader(key, additionalHeaders[key]);
688777
}
689778

690779
request.onreadystatechange = function () {
@@ -694,10 +783,7 @@ var xhrRequest = function (method, uri, data, additionalHeaders, successCallback
694783
case 201: // Success with content returned in response body.
695784
case 204: // Success with no content returned in response body.
696785
case 304: {// Success with Not Modified
697-
var responseData = null;
698-
if (request.responseText) {
699-
responseData = JSON.parse(request.responseText, dateReviver);
700-
}
786+
var responseData = parseResponse(request.responseText);
701787

702788
var response = {
703789
data: responseData,
@@ -747,7 +833,7 @@ var xhrRequest = function (method, uri, data, additionalHeaders, successCallback
747833
module.exports = xhrRequest;
748834

749835
/***/ }),
750-
/* 9 */
836+
/* 10 */
751837
/***/ (function(module, exports) {
752838

753839
/**
@@ -783,7 +869,7 @@ module.exports = function buildFunctionParameters(parameters) {
783869
};
784870

785871
/***/ }),
786-
/* 10 */
872+
/* 11 */
787873
/***/ (function(module, exports, __webpack_require__) {
788874

789875
var DWA = __webpack_require__(0);
@@ -850,7 +936,7 @@ module.exports = function buildPreferHeader(request, functionName, config) {
850936
}
851937

852938
/***/ }),
853-
/* 11 */
939+
/* 12 */
854940
/***/ (function(module, exports) {
855941

856942
/**
@@ -872,7 +958,7 @@ module.exports = function convertToReferenceObject(responseData) {
872958
}
873959

874960
/***/ }),
875-
/* 12 */
961+
/* 13 */
876962
/***/ (function(module, exports) {
877963

878964
/**
@@ -909,7 +995,7 @@ module.exports = function getFetchXmlPagingCookie(pageCookies, currentPageNumber
909995
}
910996

911997
/***/ }),
912-
/* 13 */
998+
/* 14 */
913999
/***/ (function(module, exports, __webpack_require__) {
9141000

9151001
var DWA = __webpack_require__(0);

dist/dynamics-web-api-callbacks.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)