Skip to content

Commit c3e01a7

Browse files
Encode special symbols in the filter query parameter. Fixes #22
1 parent 5560906 commit c3e01a7

File tree

8 files changed

+184
-64
lines changed

8 files changed

+184
-64
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impersonate | String | All | A String representing the GUID value for the Dynami
202202
includeAnnotations | String | `retrieveRequest`, `retrieveMultipleRequest`, `retrieveAllRequest`, `createRequest`, `updateRequest`, `upsertRequest` | Sets Prefer header with value "odata.include-annotations=" and the specified annotation. Annotations provide additional information about lookups, options sets and other complex attribute types.
203203
key | String | `retrieveRequest`, `createRequest`, `updateRequest`, `upsertRequest`, `deleteRequest` | `v.1.3.4+` A String representing collection record's Primary Key (GUID) or Alternate Key(s).
204204
maxPageSize | Number | `retrieveMultipleRequest`, `retrieveAllRequest` | Sets the odata.maxpagesize preference value to request the number of entities returned in the response.
205-
mergeLabels | Boolean | `updateRequest` | `v.1.4.2+` **Metadata Update only!** Sets `MSCRM.MergeLabels` header that controls whether to overwrite the existing labels or merge your new label with any existing language labels. [More info](https://msdn.microsoft.com/en-us/library/mt593078.aspx#Update%20entities)
205+
mergeLabels | Boolean | `updateRequest` | `v.1.4.2+` **Metadata Update only!** Sets `MSCRM.MergeLabels` header that controls whether to overwrite the existing labels or merge your new label with any existing language labels. Default value is `false`. [More info](https://msdn.microsoft.com/en-us/library/mt593078.aspx#bkmk_updateEntities)
206206
navigationProperty | String | `retrieveRequest` | A String representing the name of a single-valued navigation property. Useful when needed to retrieve information about a related record in a single request.
207207
noCache | Boolean | All | `v.1.4.0+` If set to `true`, DynamicsWebApi adds a request header `Cache-Control: no-cache`. Default value is `false`.
208208
orderBy | Array | `retrieveMultipleRequest`, `retrieveAllRequest` | An Array (of Strings) representing the order in which items are returned using the $orderby system query option. Use the asc or desc suffix to specify ascending or descending order respectively. The default is ascending if the suffix isn't applied.

dist/dynamics-web-api-callbacks.js

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,24 @@ var buildPreferHeader = __webpack_require__(12);
369369
* @property {boolean} async
370370
*/
371371

372+
/**
373+
* @param {Array} array
374+
* @param {boolean} [performJoin]
375+
* @param {boolean} [joinSymbol]
376+
*/
377+
function encodeURIComponentArray(array, performJoin, joinSymbol) {
378+
performJoin = performJoin == null ? true : performJoin;
379+
joinSymbol = joinSymbol || ',';
380+
381+
for (var i = 0; i < array.length; i++) {
382+
array[i] = encodeURIComponent(array[i]);
383+
}
384+
385+
return performJoin
386+
? array.join(joinSymbol)
387+
: array;
388+
}
389+
372390
/**
373391
* Converts optional parameters of the request to URL. If expand parameter exists this function is called recursively.
374392
*
@@ -417,7 +435,7 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
417435
ErrorHelper.stringParameterCheck(request.filter, 'DynamicsWebApi.' + functionName, "request.filter");
418436
var removeBracketsFromGuidReg = /[^"']{([\w\d]{8}[-]?(?:[\w\d]{4}[-]?){3}[\w\d]{12})}(?:[^"']|$)/g;
419437
var filterResult = request.filter.replace(removeBracketsFromGuidReg, ' $1 ').trim();
420-
requestArray.push("$filter=" + filterResult);
438+
requestArray.push("$filter=" + encodeURIComponent(filterResult));
421439
}
422440

423441
if (request.savedQuery) {
@@ -480,8 +498,6 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
480498

481499
if (request.entity) {
482500
ErrorHelper.parameterCheck(request.entity, 'DynamicsWebApi.' + functionName, 'request.entity');
483-
484-
485501
}
486502

487503
if (request.data) {
@@ -493,7 +509,7 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
493509
headers['Cache-Control'] = 'no-cache';
494510
}
495511

496-
if (request.mergeLabels){
512+
if (request.mergeLabels) {
497513
ErrorHelper.boolParameterCheck(request.mergeLabels, 'DynamicsWebApi.' + functionName, 'request.mergeLabels');
498514
headers['MSCRM.MergeLabels'] = 'true';
499515
}
@@ -571,7 +587,7 @@ function convertRequest(request, functionName, config) {
571587
}
572588
else
573589
if (result.query) {
574-
result.url += "?" + encodeURI(result.query);
590+
result.url += "?" + result.query;
575591
}
576592
}
577593
else {
@@ -2164,30 +2180,18 @@ function parseBatchResponse(response) {
21642180
return result;
21652181
}
21662182

2167-
function populateFormattedValues(object) {
2168-
var keys = Object.keys(object);
2169-
//object._dwa_extendedProperties = [];
2170-
2171-
for (var i = 0; i < keys.length; i++) {
2172-
if (object[keys[i]] != null && object[keys[i]].constructor === Array) {
2173-
for (var j = 0; j < object[keys[i]].length; j++) {
2174-
object[keys[i]][j] = populateFormattedValues(object[keys[i]][j]);
2175-
}
2176-
}
2177-
2178-
if (keys[i].indexOf('@') == -1)
2179-
continue;
2180-
2181-
var format = keys[i].split('@');
2182-
var newKey = null;
2183+
function getFormattedKeyValue(keyName, value) {
2184+
var newKey = null;
2185+
if (keyName.indexOf('@') !== -1) {
2186+
var format = keyName.split('@');
21832187
switch (format[1]) {
21842188
case 'odata.context':
21852189
newKey = 'oDataContext';
21862190
break;
21872191
case 'odata.count':
21882192
newKey = 'oDataCount';
2189-
object[keys[i]] = object[keys[i]] != null
2190-
? parseInt(object[keys[i]])
2193+
value = value != null
2194+
? parseInt(value)
21912195
: 0;
21922196
break;
21932197
case 'odata.nextLink':
@@ -2203,10 +2207,50 @@ function populateFormattedValues(object) {
22032207
newKey = format[0] + '_LogicalName';
22042208
break;
22052209
}
2210+
}
2211+
2212+
return [newKey, value];
2213+
}
2214+
2215+
function parseData(object) {
2216+
var keys = Object.keys(object);
2217+
2218+
for (var i = 0; i < keys.length; i++) {
2219+
var currentKey = keys[i];
2220+
2221+
if (object[currentKey] != null && object[currentKey].constructor === Array) {
2222+
for (var j = 0; j < object[currentKey].length; j++) {
2223+
object[currentKey][j] = parseData(object[currentKey][j]);
2224+
}
2225+
}
22062226

2207-
if (newKey) {
2208-
object[newKey] = object[keys[i]];
2209-
//object._dwa_extendedProperties.push(newKey);
2227+
//parse formatted values
2228+
var formattedKeyValue = getFormattedKeyValue(currentKey, object[currentKey]);
2229+
if (formattedKeyValue[0]) {
2230+
object[formattedKeyValue[0]] = formattedKeyValue[1];
2231+
}
2232+
2233+
//parse aliased values
2234+
if (currentKey.indexOf('_x002e_') !== -1) {
2235+
var aliasKeys = currentKey.split('_x002e_');
2236+
2237+
if (!object.hasOwnProperty(aliasKeys[0])) {
2238+
object[aliasKeys[0]] = { _dwaType: 'alias' };
2239+
}
2240+
//throw an error if there is already a property which is not an 'alias'
2241+
else if (
2242+
typeof (object[aliasKeys[0]]) !== 'object' ||
2243+
typeof (object[aliasKeys[0]]) === 'object' && !object[aliasKeys[0]].hasOwnProperty('_dwaType')) {
2244+
throw new Error('The alias name of the linked entity must be unique!');
2245+
}
2246+
2247+
object[aliasKeys[0]][aliasKeys[1]] = object[currentKey];
2248+
2249+
//aliases also contain formatted values
2250+
formattedKeyValue = getFormattedKeyValue(aliasKeys[1], object[currentKey]);
2251+
if (formattedKeyValue[0]) {
2252+
object[aliasKeys[0]][formattedKeyValue[0]] = formattedKeyValue[1];
2253+
}
22102254
}
22112255
}
22122256

@@ -2224,7 +2268,7 @@ module.exports = function parseResponse(response) {
22242268
? responseData = parseBatchResponse(response)[0]
22252269
: responseData = JSON.parse(response, dateReviver);
22262270

2227-
responseData = populateFormattedValues(responseData);
2271+
responseData = parseData(responseData);
22282272
}
22292273

22302274
return responseData;

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.

dist/dynamics-web-api.js

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,28 @@ function DynamicsWebApi(config) {
10121012
return this.retrieveRequest(request);
10131013
};
10141014

1015+
//this.retrieveAttributes = function (entityDefinitionId, attributeType, select, filter) {
1016+
1017+
// var navigationProperty = 'Attributes';
1018+
1019+
// if (attributeType) {
1020+
// ErrorHelper.stringParameterCheck(attributeType, "DynamicsWebApi.retrieveAttributes", "attributeType");
1021+
// navigationProperty += '/' + attributeType;
1022+
// }
1023+
1024+
// var request = {
1025+
// collection: 'EntityDefinitions',
1026+
// key: entityDefinitionId,
1027+
// navigationProperty: 'Attributes',
1028+
// select: select,
1029+
// filter: filter
1030+
// };
1031+
1032+
// return this.retrieveRequest(request);
1033+
//};
1034+
1035+
//this.retrieveAttribute = function(entityDefinitionId, attributeId, attributeType, select, expand
1036+
10151037
/**
10161038
* Sends an asynchronous request to update a record.
10171039
*
@@ -1792,30 +1814,18 @@ function parseBatchResponse(response) {
17921814
return result;
17931815
}
17941816

1795-
function populateFormattedValues(object) {
1796-
var keys = Object.keys(object);
1797-
//object._dwa_extendedProperties = [];
1798-
1799-
for (var i = 0; i < keys.length; i++) {
1800-
if (object[keys[i]] != null && object[keys[i]].constructor === Array) {
1801-
for (var j = 0; j < object[keys[i]].length; j++) {
1802-
object[keys[i]][j] = populateFormattedValues(object[keys[i]][j]);
1803-
}
1804-
}
1805-
1806-
if (keys[i].indexOf('@') == -1)
1807-
continue;
1808-
1809-
var format = keys[i].split('@');
1810-
var newKey = null;
1817+
function getFormattedKeyValue(keyName, value) {
1818+
var newKey = null;
1819+
if (keyName.indexOf('@') !== -1) {
1820+
var format = keyName.split('@');
18111821
switch (format[1]) {
18121822
case 'odata.context':
18131823
newKey = 'oDataContext';
18141824
break;
18151825
case 'odata.count':
18161826
newKey = 'oDataCount';
1817-
object[keys[i]] = object[keys[i]] != null
1818-
? parseInt(object[keys[i]])
1827+
value = value != null
1828+
? parseInt(value)
18191829
: 0;
18201830
break;
18211831
case 'odata.nextLink':
@@ -1831,10 +1841,50 @@ function populateFormattedValues(object) {
18311841
newKey = format[0] + '_LogicalName';
18321842
break;
18331843
}
1844+
}
1845+
1846+
return [newKey, value];
1847+
}
1848+
1849+
function parseData(object) {
1850+
var keys = Object.keys(object);
1851+
1852+
for (var i = 0; i < keys.length; i++) {
1853+
var currentKey = keys[i];
1854+
1855+
if (object[currentKey] != null && object[currentKey].constructor === Array) {
1856+
for (var j = 0; j < object[currentKey].length; j++) {
1857+
object[currentKey][j] = parseData(object[currentKey][j]);
1858+
}
1859+
}
1860+
1861+
//parse formatted values
1862+
var formattedKeyValue = getFormattedKeyValue(currentKey, object[currentKey]);
1863+
if (formattedKeyValue[0]) {
1864+
object[formattedKeyValue[0]] = formattedKeyValue[1];
1865+
}
1866+
1867+
//parse aliased values
1868+
if (currentKey.indexOf('_x002e_') !== -1) {
1869+
var aliasKeys = currentKey.split('_x002e_');
1870+
1871+
if (!object.hasOwnProperty(aliasKeys[0])) {
1872+
object[aliasKeys[0]] = { _dwaType: 'alias' };
1873+
}
1874+
//throw an error if there is already a property which is not an 'alias'
1875+
else if (
1876+
typeof (object[aliasKeys[0]]) !== 'object' ||
1877+
typeof (object[aliasKeys[0]]) === 'object' && !object[aliasKeys[0]].hasOwnProperty('_dwaType')) {
1878+
throw new Error('The alias name of the linked entity must be unique!');
1879+
}
1880+
1881+
object[aliasKeys[0]][aliasKeys[1]] = object[currentKey];
18341882

1835-
if (newKey) {
1836-
object[newKey] = object[keys[i]];
1837-
//object._dwa_extendedProperties.push(newKey);
1883+
//aliases also contain formatted values
1884+
formattedKeyValue = getFormattedKeyValue(aliasKeys[1], object[currentKey]);
1885+
if (formattedKeyValue[0]) {
1886+
object[aliasKeys[0]][formattedKeyValue[0]] = formattedKeyValue[1];
1887+
}
18381888
}
18391889
}
18401890

@@ -1852,7 +1902,7 @@ module.exports = function parseResponse(response) {
18521902
? responseData = parseBatchResponse(response)[0]
18531903
: responseData = JSON.parse(response, dateReviver);
18541904

1855-
responseData = populateFormattedValues(responseData);
1905+
responseData = parseData(responseData);
18561906
}
18571907

18581908
return responseData;
@@ -1987,6 +2037,24 @@ var buildPreferHeader = __webpack_require__(12);
19872037
* @property {boolean} async
19882038
*/
19892039

2040+
/**
2041+
* @param {Array} array
2042+
* @param {boolean} [performJoin]
2043+
* @param {boolean} [joinSymbol]
2044+
*/
2045+
function encodeURIComponentArray(array, performJoin, joinSymbol) {
2046+
performJoin = performJoin == null ? true : performJoin;
2047+
joinSymbol = joinSymbol || ',';
2048+
2049+
for (var i = 0; i < array.length; i++) {
2050+
array[i] = encodeURIComponent(array[i]);
2051+
}
2052+
2053+
return performJoin
2054+
? array.join(joinSymbol)
2055+
: array;
2056+
}
2057+
19902058
/**
19912059
* Converts optional parameters of the request to URL. If expand parameter exists this function is called recursively.
19922060
*
@@ -2035,7 +2103,7 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
20352103
ErrorHelper.stringParameterCheck(request.filter, 'DynamicsWebApi.' + functionName, "request.filter");
20362104
var removeBracketsFromGuidReg = /[^"']{([\w\d]{8}[-]?(?:[\w\d]{4}[-]?){3}[\w\d]{12})}(?:[^"']|$)/g;
20372105
var filterResult = request.filter.replace(removeBracketsFromGuidReg, ' $1 ').trim();
2038-
requestArray.push("$filter=" + filterResult);
2106+
requestArray.push("$filter=" + encodeURIComponent(filterResult));
20392107
}
20402108

20412109
if (request.savedQuery) {
@@ -2098,8 +2166,6 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
20982166

20992167
if (request.entity) {
21002168
ErrorHelper.parameterCheck(request.entity, 'DynamicsWebApi.' + functionName, 'request.entity');
2101-
2102-
21032169
}
21042170

21052171
if (request.data) {
@@ -2111,7 +2177,7 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
21112177
headers['Cache-Control'] = 'no-cache';
21122178
}
21132179

2114-
if (request.mergeLabels){
2180+
if (request.mergeLabels) {
21152181
ErrorHelper.boolParameterCheck(request.mergeLabels, 'DynamicsWebApi.' + functionName, 'request.mergeLabels');
21162182
headers['MSCRM.MergeLabels'] = 'true';
21172183
}
@@ -2189,7 +2255,7 @@ function convertRequest(request, functionName, config) {
21892255
}
21902256
else
21912257
if (result.query) {
2192-
result.url += "?" + encodeURI(result.query);
2258+
result.url += "?" + result.query;
21932259
}
21942260
}
21952261
else {

dist/dynamics-web-api.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.

lib/dynamics-web-api.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ var dwaRequest = function () {
3939
impersonate: "",
4040
navigationProperty: "",
4141
savedQuery: "",
42-
userQuery: ""
42+
userQuery: "",
43+
mergeLabels: false
4344
}
4445
};
4546
/* develblock:end */

lib/utilities/RequestConverter.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
6464
ErrorHelper.stringParameterCheck(request.filter, 'DynamicsWebApi.' + functionName, "request.filter");
6565
var removeBracketsFromGuidReg = /[^"']{([\w\d]{8}[-]?(?:[\w\d]{4}[-]?){3}[\w\d]{12})}(?:[^"']|$)/g;
6666
var filterResult = request.filter.replace(removeBracketsFromGuidReg, ' $1 ').trim();
67-
requestArray.push("$filter=" + filterResult);
67+
requestArray.push("$filter=" + encodeURIComponent(filterResult));
6868
}
6969

7070
if (request.savedQuery) {
@@ -138,7 +138,7 @@ function convertRequestOptions(request, functionName, url, joinSymbol, config) {
138138
headers['Cache-Control'] = 'no-cache';
139139
}
140140

141-
if (request.mergeLabels){
141+
if (request.mergeLabels) {
142142
ErrorHelper.boolParameterCheck(request.mergeLabels, 'DynamicsWebApi.' + functionName, 'request.mergeLabels');
143143
headers['MSCRM.MergeLabels'] = 'true';
144144
}
@@ -216,7 +216,7 @@ function convertRequest(request, functionName, config) {
216216
}
217217
else
218218
if (result.query) {
219-
result.url += "?" + encodeURI(result.query);
219+
result.url += "?" + result.query;
220220
}
221221
}
222222
else {

0 commit comments

Comments
 (0)