Skip to content

Commit 1329ec1

Browse files
add: coversion for batch requests
1 parent 5335555 commit 1329ec1

File tree

9 files changed

+207
-77
lines changed

9 files changed

+207
-77
lines changed

DynamicsWebApi.njsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<Compile Include="lib\requests\helpers\parseResponse.js">
6262
<SubType>Code</SubType>
6363
</Compile>
64+
<Compile Include="lib\utilities\BatchConverter.js" />
6465
<Compile Include="lib\utilities\buildFunctionParameters.js">
6566
<SubType>Code</SubType>
6667
</Compile>

lib/dynamics-web-api-callbacks.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ var dwaRequest = function () {
6060
/**
6161
* Dynamics Web Api Request
6262
* @typedef {Object} DWARequest
63-
* @property {boolean} async - XHR requests only! Indicates whether the requests should be made synchronously or asynchronously. Default value is true (asynchronously).
63+
* @property {boolean} async - XHR requests only! Indicates whether the requests should be made synchronously or asynchronously. Default value is 'true' (asynchronously).
6464
* @property {string} collection - The name of the Entity Collection or Entity Logical name.
6565
* @property {string} id - A String representing the Primary Key (GUID) of the record.
6666
* @property {Array} select - An Array (of Strings) representing the $select OData System Query Option to control which attributes will be returned.
@@ -84,20 +84,18 @@ var dwaRequest = function () {
8484
* @property {string} savedQuery - A String representing the GUID value of the saved query.
8585
* @property {string} userQuery - A String representing the GUID value of the user query.
8686
* @property {boolean} mergeLabels - If set to 'true', DynamicsWebApi adds a request header 'MSCRM.MergeLabels: true'. Default value is 'false'
87+
* @property {boolean} isBatch - If set to 'true', DynamicsWebApi treats a request as a part of a batch request. Call ExecuteBatch to execute all requests in a batch. Default value is 'false'.
8788
*/
8889

8990
/**
9091
* Constructor.
9192
* @constructor
9293
* @param {DWAConfig} [config] - configuration object
9394
* @example
94-
//Empty constructor (will work only inside CRM/D365)
9595
*var dynamicsWebApi = new DynamicsWebApi();
9696
* @example
97-
//Constructor with a configuration parameter (only for CRM/D365)
98-
*var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: '9.0' });
97+
* var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: '9.0' });
9998
* @example
100-
//Constructor with a configuration parameter for CRM/D365 and Node.js
10199
*var dynamicsWebApi = new DynamicsWebApi({
102100
* webApiUrl: 'https:/myorg.api.crm.dynamics.com/api/data/v9.0/',
103101
* includeAnnotations: 'OData.Community.Display.V1.FormattedValue'

lib/dynamics-web-api.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
var DWA = require("./dwa");
1+
"use strict";
2+
3+
var DWA = require("./dwa");
24
var Utility = require('./utilities/Utility');
35
var ErrorHelper = require('./helpers/ErrorHelper');
46
var Request = require('./requests/sendRequest');
@@ -61,7 +63,7 @@ var dwaRequest = function () {
6163
/**
6264
* Dynamics Web Api Request
6365
* @typedef {Object} DWARequest
64-
* @property {boolean} async - XHR requests only! Indicates whether the requests should be made synchronously or asynchronously. Default value is true (asynchronously).
66+
* @property {boolean} async - XHR requests only! Indicates whether the requests should be made synchronously or asynchronously. Default value is 'true' (asynchronously).
6567
* @property {string} collection - The name of the Entity Collection or Entity Logical name.
6668
* @property {string} id - A String representing the Primary Key (GUID) of the record.
6769
* @property {Array} select - An Array (of Strings) representing the $select OData System Query Option to control which attributes will be returned.
@@ -84,21 +86,19 @@ var dwaRequest = function () {
8486
* @property {boolean} noCache - If set to 'true', DynamicsWebApi adds a request header 'Cache-Control: no-cache'. Default value is 'false'.
8587
* @property {string} savedQuery - A String representing the GUID value of the saved query.
8688
* @property {string} userQuery - A String representing the GUID value of the user query.
87-
* @property {boolean} mergeLabels - If set to 'true', DynamicsWebApi adds a request header 'MSCRM.MergeLabels: true'. Default value is 'false'
89+
* @property {boolean} mergeLabels - If set to 'true', DynamicsWebApi adds a request header 'MSCRM.MergeLabels: true'. Default value is 'false'.
90+
* @property {boolean} isBatch - If set to 'true', DynamicsWebApi treats a request as a part of a batch request. Call ExecuteBatch to execute all requests in a batch. Default value is 'false'.
8891
*/
8992

9093
/**
9194
* Constructor.
9295
* @constructor
9396
* @param {DWAConfig} [config] - configuration object
9497
* @example
95-
//Empty constructor (will work only inside CRM/D365)
9698
*var dynamicsWebApi = new DynamicsWebApi();
9799
* @example
98-
//Constructor with a configuration parameter (only for CRM/D365)
99100
*var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: '9.0' });
100101
* @example
101-
//Constructor with a configuration parameter for CRM/D365 and Node.js
102102
*var dynamicsWebApi = new DynamicsWebApi({
103103
* webApiUrl: 'https:/myorg.api.crm.dynamics.com/api/data/v9.0/',
104104
* includeAnnotations: 'OData.Community.Display.V1.FormattedValue'
@@ -116,6 +116,8 @@ function DynamicsWebApi(config) {
116116
returnRepresentation: null
117117
};
118118

119+
var _isBatch = false;
120+
119121
if (!config) {
120122
config = _internalConfig;
121123
}
@@ -174,6 +176,7 @@ function DynamicsWebApi(config) {
174176
this.setConfig(config);
175177

176178
var _makeRequest = function (method, request, functionName) {
179+
request.isBatch = _isBatch;
177180
return new Promise(function (resolve, reject) {
178181
Request.makeRequest(method, request, functionName, _internalConfig, resolve, reject);
179182
});
@@ -987,7 +990,7 @@ function DynamicsWebApi(config) {
987990
* @param {string} actionName - The name of the Web API action.
988991
* @param {Object} [requestObject] - Action request body object.
989992
* @param {string} [impersonateUserId] - A String representing the GUID value for the Dynamics 365 system user id. Impersonates the user.
990-
* @returns {Promise} D365 Web Api result
993+
* @returns {Promise | Function} D365 Web Api result
991994
*/
992995
this.executeBoundAction = function (id, collection, actionName, requestObject, impersonateUserId) {
993996
return _executeAction(actionName, requestObject, collection, id, impersonateUserId);
@@ -1006,11 +1009,14 @@ function DynamicsWebApi(config) {
10061009
data: requestObject
10071010
};
10081011

1009-
return _makeRequest("POST", request, 'executeAction').then(function (response) {
1012+
var onSuccess = function (response) {
10101013
if (response.data) {
10111014
return response.data;
10121015
}
1013-
});
1016+
};
1017+
1018+
1019+
return _makeRequest("POST", request, 'executeAction').then(onSuccess);
10141020
};
10151021

10161022
/**
@@ -1400,6 +1406,15 @@ function DynamicsWebApi(config) {
14001406
return this.retrieveMultipleRequest(request);
14011407
};
14021408

1409+
this.batch = function () {
1410+
_isBatch = true;
1411+
};
1412+
1413+
this.executeBatch = function () {
1414+
_isBatch = false;
1415+
return _makeRequest('POST', { collection: '$batch' }, 'executeBatch');
1416+
};
1417+
14031418
/**
14041419
* Creates a new instance of DynamicsWebApi
14051420
*

lib/requests/helpers/parseResponse.js

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,6 @@ if (!String.prototype.endsWith || !String.prototype.startsWith) {
66
require("../../polyfills/string-es6");
77
}
88

9-
//https://github.com/emiltholin/google-api-batch-utils
10-
function parseBatchResponse(response) {
11-
// Not the same delimiter in the response as we specify ourselves in the request,
12-
// so we have to extract it.
13-
var delimiter = response.substr(0, response.indexOf('\r\n'));
14-
var parts = response.split(delimiter);
15-
// The first part will always be an empty string. Just remove it.
16-
parts.shift();
17-
// The last part will be the "--". Just remove it.
18-
parts.pop();
19-
20-
var result = [];
21-
for (var i = 0; i < parts.length; i++) {
22-
var part = parts[i];
23-
var p = part.substring(part.indexOf("{"), part.lastIndexOf("}") + 1);
24-
result.push(JSON.parse(p, dateReviver));
25-
}
26-
return result;
27-
}
28-
299
function getFormattedKeyValue(keyName, value) {
3010
var newKey = null;
3111
if (keyName.indexOf('@') !== -1) {
@@ -85,8 +65,8 @@ function parseData(object) {
8565
}
8666
//throw an error if there is already a property which is not an 'alias'
8767
else if (
88-
typeof (object[aliasKeys[0]]) !== 'object' ||
89-
typeof (object[aliasKeys[0]]) === 'object' && !object[aliasKeys[0]].hasOwnProperty('_dwaType')) {
68+
typeof object[aliasKeys[0]] !== 'object' ||
69+
typeof object[aliasKeys[0]] === 'object' && !object[aliasKeys[0]].hasOwnProperty('_dwaType')) {
9070
throw new Error('The alias name of the linked entity must be unique!');
9171
}
9272

@@ -103,19 +83,51 @@ function parseData(object) {
10383
return object;
10484
}
10585

86+
//partially taken from https://github.com/emiltholin/google-api-batch-utils
87+
function parseBatchResponse(response) {
88+
// Not the same delimiter in the response as we specify ourselves in the request,
89+
// so we have to extract it.
90+
var delimiter = response.substr(0, response.indexOf('\r\n'));
91+
var batchResponseParts = response.split(delimiter);
92+
// The first part will always be an empty string. Just remove it.
93+
batchResponseParts.shift();
94+
// The last part will be the "--". Just remove it.
95+
batchResponseParts.pop();
96+
97+
var result = [];
98+
for (var i = 0; i < batchResponseParts.length; i++) {
99+
var batchResponse = batchResponseParts[i];
100+
if (batchResponse.indexOf('--changesetresponse_') > -1) {
101+
batchResponse = batchResponse.trim();
102+
var batchToProcess = batchResponse
103+
.substring(batchResponse.indexOf('\r\n') + 1).trim();
104+
105+
result = result.concat(parseBatchResponse(batchToProcess));
106+
}
107+
else {
108+
var responseData = batchResponse.substring(batchResponse.indexOf("{"), batchResponse.lastIndexOf("}") + 1);
109+
result.push(parseData(JSON.parse(responseData, dateReviver)));
110+
}
111+
}
112+
return result;
113+
}
114+
106115
/**
107116
*
108-
* @param {string} response
117+
* @param {string} response - response that needs to be parsed
118+
* @returns {any} parsed response
109119
*/
110-
module.exports = function parseResponse(response) {
111-
var responseData = null;
120+
module.exports = function parseResponse(response, convertedToBatch) {
112121
if (response.length) {
113-
responseData = response.indexOf('--batchresponse_') > -1
114-
? responseData = parseBatchResponse(response)[0]
115-
: responseData = JSON.parse(response, dateReviver);
122+
if (response.indexOf('--batchresponse_') > -1) {
123+
var batch = parseBatchResponse(response);
116124

117-
responseData = parseData(responseData);
125+
return convertedToBatch
126+
? batch[0]
127+
: batch;
128+
}
129+
else {
130+
return parseData(JSON.parse(response, dateReviver));
131+
}
118132
}
119-
120-
return responseData;
121-
}
133+
};

lib/requests/http.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,8 @@ var parseResponse = require('./helpers/parseResponse');
1313
* @param {string} [data] - Data to send in the request.
1414
* @param {Object} [additionalHeaders] - Additional headers. IMPORTANT! This object does not contain default headers needed for every request.
1515
*/
16-
var httpRequest = function (method, uri, data, additionalHeaders, successCallback, errorCallback) {
16+
var httpRequest = function (method, uri, data, additionalHeaders, successCallback, errorCallback, async, convertedToBatch) {
1717
var headers = {};
18-
// "Accept": "application/json",
19-
// "OData-MaxVersion": "4.0",
20-
// "OData-Version": "4.0"
21-
//};
2218

2319
if (data) {
2420
headers["Content-Type"] = additionalHeaders['Content-Type'];
@@ -40,9 +36,7 @@ var httpRequest = function (method, uri, data, additionalHeaders, successCallbac
4036
port: parsedUrl.port,
4137
path: parsedUrl.path,
4238
method: method,
43-
headers: headers,
44-
//agent: agent,
45-
//auth: auth
39+
headers: headers
4640
};
4741

4842
var interface = isHttp ? http : https;
@@ -59,7 +53,7 @@ var httpRequest = function (method, uri, data, additionalHeaders, successCallbac
5953
case 201: // Success with content returned in response body.
6054
case 204: // Success with no content returned in response body.
6155
case 304: {// Success with Not Modified
62-
var responseData = parseResponse(rawData);
56+
var responseData = parseResponse(rawData, convertedToBatch);
6357

6458
var response = {
6559
data: responseData,

0 commit comments

Comments
 (0)