@shko.online/dataverse-odata 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/lib/cjs/getExpandFromParser.js +19 -12
  3. package/lib/cjs/getFetchXmlFromParser.js +37 -35
  4. package/lib/cjs/getOrderByFromParser.js +28 -25
  5. package/lib/cjs/getSelectFromParser.js +13 -4
  6. package/lib/cjs/getTopFromParser.js +25 -21
  7. package/lib/cjs/getXQueryFromParser.js +9 -19
  8. package/lib/cjs/parseOData.js +2 -18
  9. package/lib/cjs/validators/atMostOnce.js +24 -0
  10. package/lib/cjs/validators/differentFromEmptyString.js +23 -0
  11. package/lib/cjs/validators/hasContent.js +24 -0
  12. package/lib/cjs/validators/isGuid.js +25 -0
  13. package/lib/cjs/validators/recognizedGuid.js +23 -0
  14. package/lib/esm/getExpandFromParser.js +18 -12
  15. package/lib/esm/getFetchXmlFromParser.js +37 -35
  16. package/lib/esm/getOrderByFromParser.js +28 -25
  17. package/lib/esm/getSelectFromParser.js +13 -4
  18. package/lib/esm/getTopFromParser.js +24 -21
  19. package/lib/esm/getXQueryFromParser.js +9 -18
  20. package/lib/esm/parseOData.js +2 -18
  21. package/lib/esm/validators/atMostOnce.js +17 -0
  22. package/lib/esm/validators/differentFromEmptyString.js +16 -0
  23. package/lib/esm/validators/hasContent.js +17 -0
  24. package/lib/esm/validators/isGuid.js +18 -0
  25. package/lib/esm/validators/recognizedGuid.js +16 -0
  26. package/lib/modern/getExpandFromParser.js +18 -12
  27. package/lib/modern/getFetchXmlFromParser.js +37 -35
  28. package/lib/modern/getOrderByFromParser.js +29 -26
  29. package/lib/modern/getSelectFromParser.js +13 -4
  30. package/lib/modern/getTopFromParser.js +24 -21
  31. package/lib/modern/getXQueryFromParser.js +9 -18
  32. package/lib/modern/parseOData.js +2 -18
  33. package/lib/modern/validators/atMostOnce.js +17 -0
  34. package/lib/modern/validators/differentFromEmptyString.js +16 -0
  35. package/lib/modern/validators/hasContent.js +17 -0
  36. package/lib/modern/validators/isGuid.js +18 -0
  37. package/lib/modern/validators/recognizedGuid.js +16 -0
  38. package/lib/ts3.4/getSelectFromParser.d.ts +1 -1
  39. package/lib/ts3.4/validators/atMostOnce.d.ts +10 -0
  40. package/lib/ts3.4/validators/differentFromEmptyString.d.ts +9 -0
  41. package/lib/ts3.4/validators/hasContent.d.ts +10 -0
  42. package/lib/ts3.4/validators/isGuid.d.ts +9 -0
  43. package/lib/ts3.4/validators/recognizedGuid.d.ts +9 -0
  44. package/lib/ts3.9/getExpandFromParser.d.ts.map +1 -1
  45. package/lib/ts3.9/getFetchXmlFromParser.d.ts.map +1 -1
  46. package/lib/ts3.9/getOrderByFromParser.d.ts.map +1 -1
  47. package/lib/ts3.9/getSelectFromParser.d.ts +1 -1
  48. package/lib/ts3.9/getSelectFromParser.d.ts.map +1 -1
  49. package/lib/ts3.9/getTopFromParser.d.ts.map +1 -1
  50. package/lib/ts3.9/getXQueryFromParser.d.ts.map +1 -1
  51. package/lib/ts3.9/parseOData.d.ts.map +1 -1
  52. package/lib/ts3.9/validators/atMostOnce.d.ts +10 -0
  53. package/lib/ts3.9/validators/atMostOnce.d.ts.map +1 -0
  54. package/lib/ts3.9/validators/differentFromEmptyString.d.ts +9 -0
  55. package/lib/ts3.9/validators/differentFromEmptyString.d.ts.map +1 -0
  56. package/lib/ts3.9/validators/hasContent.d.ts +10 -0
  57. package/lib/ts3.9/validators/hasContent.d.ts.map +1 -0
  58. package/lib/ts3.9/validators/isGuid.d.ts +9 -0
  59. package/lib/ts3.9/validators/isGuid.d.ts.map +1 -0
  60. package/lib/ts3.9/validators/recognizedGuid.d.ts +9 -0
  61. package/lib/ts3.9/validators/recognizedGuid.d.ts.map +1 -0
  62. package/package.json +1 -1
  63. package/src/getExpandFromParser.ts +23 -12
  64. package/src/getFetchXmlFromParser.ts +48 -45
  65. package/src/getOrderByFromParser.ts +30 -26
  66. package/src/getSelectFromParser.ts +13 -4
  67. package/src/getTopFromParser.ts +25 -21
  68. package/src/getXQueryFromParser.ts +10 -19
  69. package/src/parseOData.ts +10 -18
  70. package/src/validators/atMostOnce.ts +19 -0
  71. package/src/validators/differentFromEmptyString.ts +18 -0
  72. package/src/validators/hasContent.ts +19 -0
  73. package/src/validators/isGuid.ts +20 -0
  74. package/src/validators/recognizedGuid.ts +18 -0
  75. package/lib/cjs/validateNotEmpty.js +0 -17
  76. package/lib/esm/validateNotEmpty.js +0 -10
  77. package/lib/modern/validateNotEmpty.js +0 -10
  78. package/lib/ts3.4/validateNotEmpty.d.ts +0 -3
  79. package/lib/ts3.9/validateNotEmpty.d.ts +0 -3
  80. package/lib/ts3.9/validateNotEmpty.d.ts.map +0 -1
  81. package/src/validateNotEmpty.ts +0 -12
@@ -1,43 +1,45 @@
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ import { differentFromEmptyString } from './validators/differentFromEmptyString';
3
+ const option = 'fetchXml';
4
+
1
5
  /**
2
6
  * Parses the {@link ODataFetch.fetchXml fetchXml} query
3
7
  * @returns Returns `false` when the parse has an error
4
8
  */
5
9
  export const getFetchXmlFromParser = (parser, result) => {
6
- const fetchXml = parser.get('fetchXml');
7
- if (fetchXml !== null) {
8
- if (fetchXml === '') {
9
- result.error = {
10
- code: '0x80040203',
11
- message: 'Expected non-empty string.'
12
- };
13
- return false;
14
- }
15
- const serializer = new DOMParser();
16
- const fetchXmlDocument = serializer.parseFromString(fetchXml, 'text/xml');
17
- if (fetchXmlDocument.documentElement.tagName === 'parsererror') {
18
- result.error = {
19
- code: '0x80040201',
20
- message: 'Invalid XML.'
21
- };
22
- return false;
23
- }
24
- const entity = fetchXmlDocument.evaluate('fetch/entity', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
25
- if (fetchXmlDocument.documentElement.children.length != 1 || !entity || !entity.getAttribute('name')) {
26
- result.error = {
27
- code: '0x80041102',
28
- message: 'Entity Name was not specified in FetchXml String.'
29
- };
30
- return false;
31
- }
32
- const invalidAttribute = fetchXmlDocument.evaluate('fetch/entity/*[not(self::filter or self::order or self::link-entity or self::attribute or self::all-attributes or self::no-attrs)]', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
33
- if (invalidAttribute) {
34
- result.error = {
35
- code: '0x8004111c',
36
- message: `Invalid Child Node, valid nodes are filter, order, link-entity, attribute, all-attributes, no-attrs. NodeName = ${invalidAttribute.tagName} NodeXml = ${invalidAttribute.outerHTML}`
37
- };
38
- return false;
39
- }
40
- result.fetchXml = fetchXmlDocument;
10
+ const value = parser.getAll(option);
11
+ if (value.length === 0) {
12
+ return true;
41
13
  }
14
+ if (!atMostOnce(option, value, result) || !differentFromEmptyString(value, result)) {
15
+ return false;
16
+ }
17
+ const fetchXml = value[0];
18
+ const serializer = new DOMParser();
19
+ const fetchXmlDocument = serializer.parseFromString(fetchXml, 'text/xml');
20
+ if (fetchXmlDocument.documentElement.tagName === 'parsererror') {
21
+ result.error = {
22
+ code: '0x80040201',
23
+ message: 'Invalid XML.'
24
+ };
25
+ return false;
26
+ }
27
+ const entity = fetchXmlDocument.evaluate('fetch/entity', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
28
+ if (fetchXmlDocument.documentElement.children.length != 1 || !entity || !entity.getAttribute('name')) {
29
+ result.error = {
30
+ code: '0x80041102',
31
+ message: 'Entity Name was not specified in FetchXml String.'
32
+ };
33
+ return false;
34
+ }
35
+ const invalidAttribute = fetchXmlDocument.evaluate('fetch/entity/*[not(self::filter or self::order or self::link-entity or self::attribute or self::all-attributes or self::no-attrs)]', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
36
+ if (invalidAttribute) {
37
+ result.error = {
38
+ code: '0x8004111c',
39
+ message: `Invalid Child Node, valid nodes are filter, order, link-entity, attribute, all-attributes, no-attrs. NodeName = ${invalidAttribute.tagName} NodeXml = ${invalidAttribute.outerHTML}`
40
+ };
41
+ return false;
42
+ }
43
+ result.fetchXml = fetchXmlDocument;
42
44
  return true;
43
45
  };
@@ -1,4 +1,6 @@
1
- import { validateNotEmpty } from './validateNotEmpty';
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ import { hasContent } from './validators/hasContent';
3
+ const option = '$orderby';
2
4
  const edmProperty = /\w{1-255}/gi;
3
5
 
4
6
  /**
@@ -6,32 +8,33 @@ const edmProperty = /\w{1-255}/gi;
6
8
  * @returns Returns `false` when the parse has an error
7
9
  */
8
10
  export const getOrderByFromParser = (parser, result) => {
9
- let $orderby = parser.get('$orderby');
10
- if ($orderby !== null) {
11
- if (!validateNotEmpty('$orderby', $orderby, result)) {
11
+ let value = parser.getAll(option);
12
+ if (value.length === 0) {
13
+ return true;
14
+ }
15
+ if (!atMostOnce(option, value, result) || !hasContent(option, value, result)) {
16
+ return false;
17
+ }
18
+ let $orderby = value[0].trimEnd();
19
+ const orderByArray = [];
20
+ for (let i = 0; i < $orderby.length; i++) {
21
+ if (false /* syntax error */) {
22
+ result.error = {
23
+ code: '0x0',
24
+ message: `Syntax error at position ${i} in '${$orderby}'.`
25
+ };
12
26
  return false;
13
27
  }
14
- $orderby = $orderby.trimEnd();
15
- const orderByArray = [];
16
- for (let i = 0; i < $orderby.length; i++) {
17
- if (false /* syntax error */) {
18
- result.error = {
19
- code: '0x0',
20
- message: `Syntax error at position ${i} in '${$orderby}'.`
21
- };
22
- return false;
23
- }
24
- }
25
- orderByArray.forEach(orderBy => {
26
- if (!orderBy.column?.match(edmProperty)) {
27
- result.error = {
28
- code: '0x80060888',
29
- message: 'Order By Property must be of type EdmProperty'
30
- };
31
- return false;
32
- }
33
- });
34
- result.$orderby = orderByArray;
35
28
  }
29
+ orderByArray.forEach(orderBy => {
30
+ if (!orderBy.column?.match(edmProperty)) {
31
+ result.error = {
32
+ code: '0x80060888',
33
+ message: 'Order By Property must be of type EdmProperty'
34
+ };
35
+ return false;
36
+ }
37
+ });
38
+ result.$orderby = orderByArray;
36
39
  return true;
37
40
  };
@@ -1,11 +1,20 @@
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ const option = '$select';
3
+
1
4
  /**
2
5
  * Parses the {@link ODataSelect.$select $select} query
3
- * @returns Returns `false` when the parse has an error
6
+ * @returns {boolean} Returns `false` when the parse has an error
4
7
  */
5
8
  export const getSelectFromParser = (parser, result) => {
6
- const $select = parser.get('$select');
7
- if ($select !== null) {
8
- result.$select = $select.split(',');
9
+ const value = parser.getAll(option);
10
+ if (value.length === 0) {
11
+ return true;
12
+ }
13
+ if (!atMostOnce(option, value, result)) {
14
+ return false;
15
+ }
16
+ if (value.length > 0) {
17
+ result.$select = value[0].split(',');
9
18
  }
10
19
  return true;
11
20
  };
@@ -1,30 +1,33 @@
1
- import { validateNotEmpty } from './validateNotEmpty';
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ import { hasContent } from './validators/hasContent';
3
+ const option = '$top';
2
4
 
3
5
  /**
4
6
  * Parses the {@link ODataTop.$top $top} query
5
7
  * @returns Returns `false` when the parse has an error
6
8
  */
7
9
  export const getTopFromParser = (parser, result) => {
8
- const $topValue = parser.get('$top');
9
- if ($topValue !== null) {
10
- if (!validateNotEmpty('$orderby', $topValue, result)) {
11
- return false;
12
- }
13
- let $top;
14
- if (!$topValue.match(/^\d+$/) || ($top = parseInt($topValue)) < 0) {
15
- result.error = {
16
- code: '0x0',
17
- message: `Invalid value '${$topValue}' for $top query option found. The $top query option requires a non-negative integer value.`
18
- };
19
- return false;
20
- } else if ($top === 0) {
21
- result.error = {
22
- code: '0x0',
23
- message: `Invalid value for $top query option.`
24
- };
25
- return false;
26
- }
27
- result.$top = $top;
10
+ const value = parser.getAll(option);
11
+ if (value.length === 0) {
12
+ return true;
28
13
  }
14
+ if (!atMostOnce(option, value, result) || !hasContent(option, value, result)) {
15
+ return false;
16
+ }
17
+ let $top;
18
+ if (!value[0].match(/^\d+$/) || ($top = parseInt(value[0])) < 0) {
19
+ result.error = {
20
+ code: '0x0',
21
+ message: `Invalid value '${value}' for $top query option found. The $top query option requires a non-negative integer value.`
22
+ };
23
+ return false;
24
+ } else if ($top === 0) {
25
+ result.error = {
26
+ code: '0x0',
27
+ message: `Invalid value for $top query option.`
28
+ };
29
+ return false;
30
+ }
31
+ result.$top = $top;
29
32
  return true;
30
33
  };
@@ -1,4 +1,5 @@
1
- const guidRegex = /[0-9A-F]{8}\-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/gi;
1
+ import { isGuid } from './validators/isGuid';
2
+ import { recognizedGuid } from './validators/recognizedGuid';
2
3
 
3
4
  /**
4
5
  * Parses the {@link ODataSavedQuery.savedQuery savedQuery} or
@@ -6,23 +7,13 @@ const guidRegex = /[0-9A-F]{8}\-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}
6
7
  * @returns Returns `false` when the parse has an error
7
8
  */
8
9
  export const getXQueryFromParser = (X, parser, result) => {
9
- const xQuery = parser.get(X);
10
- if (xQuery !== null) {
11
- if (!xQuery.trim()) {
12
- result.error = {
13
- code: '0x0',
14
- message: 'Unrecognized Guid format.'
15
- };
16
- return false;
17
- }
18
- if (!xQuery.match(guidRegex)) {
19
- result.error = {
20
- code: '0x0',
21
- message: 'Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).'
22
- };
23
- return false;
24
- }
25
- result[X] = xQuery;
10
+ const value = parser.getAll(X);
11
+ if (value.length === 0) {
12
+ return true;
26
13
  }
14
+ if (!recognizedGuid(value, result) || !isGuid(value, result)) {
15
+ return false;
16
+ }
17
+ result[X] = value[0];
27
18
  return true;
28
19
  };
@@ -3,6 +3,7 @@ import { getSelectFromParser } from './getSelectFromParser';
3
3
  import { getExpandFromParser } from './getExpandFromParser';
4
4
  import { getFetchXmlFromParser } from './getFetchXmlFromParser';
5
5
  import { getXQueryFromParser } from './getXQueryFromParser';
6
+ import { getOrderByFromParser } from './getOrderByFromParser';
6
7
 
7
8
  /**
8
9
  * parses the OData query and applies some Dataverse validations
@@ -12,23 +13,6 @@ import { getXQueryFromParser } from './getXQueryFromParser';
12
13
  export const parseOData = query => {
13
14
  const parser = new URLSearchParams(query);
14
15
  const result = {};
15
- if (!getExpandFromParser(parser, result)) {
16
- return result;
17
- }
18
- if (!getSelectFromParser(parser, result)) {
19
- return result;
20
- }
21
- if (!getTopFromParser(parser, result)) {
22
- return result;
23
- }
24
- if (!getFetchXmlFromParser(parser, result)) {
25
- return result;
26
- }
27
- if (!getXQueryFromParser('savedQuery', parser, result)) {
28
- return result;
29
- }
30
- if (!getXQueryFromParser('userQuery', parser, result)) {
31
- return result;
32
- }
16
+ getExpandFromParser(parser, result) && getSelectFromParser(parser, result) && getTopFromParser(parser, result) && getFetchXmlFromParser(parser, result) && getXQueryFromParser('savedQuery', parser, result) && getXQueryFromParser('userQuery', parser, result) && getOrderByFromParser(parser, result);
33
17
  return result;
34
18
  };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Options of this type must be specified at most once.
3
+ * @param option The option being validated (ex. $top)
4
+ * @param value The result of {@link URLSearchParams.getAll getAll}
5
+ * @param result The {@link ODataQuery} to append the error to
6
+ * @returns {boolean} Returns `false` when the parse has an error
7
+ */
8
+ export const atMostOnce = (option, value, result) => {
9
+ if (value.length > 1) {
10
+ result.error = {
11
+ code: '0x0',
12
+ message: `Query option '${option}' was specified more than once, but it must be specified at most once.`
13
+ };
14
+ return false;
15
+ }
16
+ return true;
17
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ *
3
+ * @param value The result of {@link URLSearchParams.getAll getAll}
4
+ * @param result The {@link ODataQuery} to append the error to
5
+ * @returns {boolean} Returns `false` when the parse has an error
6
+ */
7
+ export const differentFromEmptyString = (value, result) => {
8
+ if (value[0] === '') {
9
+ result.error = {
10
+ code: '0x80040203',
11
+ message: 'Expected non-empty string.'
12
+ };
13
+ return false;
14
+ }
15
+ return true;
16
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Options of this type must be specified at most once.
3
+ * @param option The option being validated (ex. $top)
4
+ * @param value The result of {@link URLSearchParams.getAll getAll}
5
+ * @param result The {@link ODataQuery} to append the error to
6
+ * @returns {boolean} Returns `false` when the parse has an error
7
+ */
8
+ export const hasContent = (query, value, result) => {
9
+ if (!value[0].trim()) {
10
+ result.error = {
11
+ code: '0x0',
12
+ message: `The value for OData query '${query}' cannot be empty.`
13
+ };
14
+ return false;
15
+ }
16
+ return true;
17
+ };
@@ -0,0 +1,18 @@
1
+ const guidRegex = /[0-9A-F]{8}\-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/gi;
2
+
3
+ /**
4
+ *
5
+ * @param value The result of {@link URLSearchParams.getAll getAll}
6
+ * @param result The {@link ODataQuery} to append the error to
7
+ * @returns {boolean} Returns `false` when the parse has an error
8
+ */
9
+ export const isGuid = (value, result) => {
10
+ if (!value[0].match(guidRegex)) {
11
+ result.error = {
12
+ code: '0x0',
13
+ message: 'Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).'
14
+ };
15
+ return false;
16
+ }
17
+ return true;
18
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ *
3
+ * @param value The result of {@link URLSearchParams.getAll getAll}
4
+ * @param result The {@link ODataQuery} to append the error to
5
+ * @returns {boolean} Returns `false` when the parse has an error
6
+ */
7
+ export const recognizedGuid = (value, result) => {
8
+ if (!value[0].trim()) {
9
+ result.error = {
10
+ code: '0x0',
11
+ message: 'Unrecognized Guid format.'
12
+ };
13
+ return false;
14
+ }
15
+ return true;
16
+ };
@@ -1,25 +1,31 @@
1
1
  import { getSelectFromParser } from './getSelectFromParser';
2
+ import { atMostOnce } from './validators/atMostOnce';
3
+ const option = '$expand';
2
4
 
3
5
  /**
4
6
  * Parses the {@link ODataExpand.$expand $expand} query
5
7
  * @returns Returns `false` when the parse has an error
6
8
  */
7
9
  export const getExpandFromParser = (parser, result) => {
8
- const $expand = parser.get('$expand');
9
- if ($expand !== null) {
10
- result.$expand = {};
11
- if (!extractExpand($expand, result)) {
12
- return false;
13
- }
10
+ const value = parser.getAll(option);
11
+ if (value.length === 0) {
12
+ return true;
13
+ }
14
+ if (!atMostOnce(option, value, result)) {
15
+ return false;
16
+ }
17
+ result.$expand = {};
18
+ if (!extractExpand(value[0], result)) {
19
+ return false;
14
20
  }
15
21
  return true;
16
22
  };
17
23
  const extractExpand = (value, $expand) => {
18
- const match = value.match(/^\s*(\w(\w|\d|_)*)\s*(,|\()?\s*/);
24
+ const match = value.match(/^\s*(\w(\w|\d|_)*)\s*(,|\(|\))?\s*/);
19
25
  if (match === null || match[0].length < value.length && match[3] === null || match[0].length === value.length && match[3] !== undefined) {
20
26
  $expand.error = {
21
27
  code: '0x0',
22
- message: 'invalid expand expression'
28
+ message: `Term '${value}' is not valid in a $select or $expand expression.`
23
29
  };
24
30
  return false;
25
31
  }
@@ -57,7 +63,7 @@ const extractExpand = (value, $expand) => {
57
63
  if (innerExpand.$expand === undefined && innerExpand.$select === undefined) {
58
64
  $expand.error = {
59
65
  code: '0x0',
60
- message: 'Empty expand'
66
+ message: `Missing expand option on navigation property '${match[1]}'. If a parenthesis expression follows an expanded navigation property, then at least one expand option must be provided.`
61
67
  };
62
68
  return false;
63
69
  }
@@ -86,7 +92,7 @@ const getClosingBracket = value => {
86
92
  const match = value.substring(startAt).match(/\(|\)/);
87
93
  if (match === null) {
88
94
  return {
89
- error: 'no closing bracket found',
95
+ error: 'Found an unbalanced bracket expression.',
90
96
  index: -1
91
97
  };
92
98
  }
@@ -100,10 +106,10 @@ const getClosingBracket = value => {
100
106
  } else {
101
107
  depth += 1;
102
108
  }
103
- startAt = (match.index || 0) + 1;
109
+ startAt += (match.index || 0) + 1;
104
110
  }
105
111
  return {
106
- error: 'no closing bracket found',
112
+ error: 'Found an unbalanced bracket expression.',
107
113
  index: -1
108
114
  };
109
115
  };
@@ -1,43 +1,45 @@
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ import { differentFromEmptyString } from './validators/differentFromEmptyString';
3
+ const option = 'fetchXml';
4
+
1
5
  /**
2
6
  * Parses the {@link ODataFetch.fetchXml fetchXml} query
3
7
  * @returns Returns `false` when the parse has an error
4
8
  */
5
9
  export const getFetchXmlFromParser = (parser, result) => {
6
- const fetchXml = parser.get('fetchXml');
7
- if (fetchXml !== null) {
8
- if (fetchXml === '') {
9
- result.error = {
10
- code: '0x80040203',
11
- message: 'Expected non-empty string.'
12
- };
13
- return false;
14
- }
15
- const serializer = new DOMParser();
16
- const fetchXmlDocument = serializer.parseFromString(fetchXml, 'text/xml');
17
- if (fetchXmlDocument.documentElement.tagName === 'parsererror') {
18
- result.error = {
19
- code: '0x80040201',
20
- message: 'Invalid XML.'
21
- };
22
- return false;
23
- }
24
- const entity = fetchXmlDocument.evaluate('fetch/entity', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
25
- if (fetchXmlDocument.documentElement.children.length != 1 || !entity || !entity.getAttribute('name')) {
26
- result.error = {
27
- code: '0x80041102',
28
- message: 'Entity Name was not specified in FetchXml String.'
29
- };
30
- return false;
31
- }
32
- const invalidAttribute = fetchXmlDocument.evaluate('fetch/entity/*[not(self::filter or self::order or self::link-entity or self::attribute or self::all-attributes or self::no-attrs)]', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
33
- if (invalidAttribute) {
34
- result.error = {
35
- code: '0x8004111c',
36
- message: `Invalid Child Node, valid nodes are filter, order, link-entity, attribute, all-attributes, no-attrs. NodeName = ${invalidAttribute.tagName} NodeXml = ${invalidAttribute.outerHTML}`
37
- };
38
- return false;
39
- }
40
- result.fetchXml = fetchXmlDocument;
10
+ const value = parser.getAll(option);
11
+ if (value.length === 0) {
12
+ return true;
41
13
  }
14
+ if (!atMostOnce(option, value, result) || !differentFromEmptyString(value, result)) {
15
+ return false;
16
+ }
17
+ const fetchXml = value[0];
18
+ const serializer = new DOMParser();
19
+ const fetchXmlDocument = serializer.parseFromString(fetchXml, 'text/xml');
20
+ if (fetchXmlDocument.documentElement.tagName === 'parsererror') {
21
+ result.error = {
22
+ code: '0x80040201',
23
+ message: 'Invalid XML.'
24
+ };
25
+ return false;
26
+ }
27
+ const entity = fetchXmlDocument.evaluate('fetch/entity', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
28
+ if (fetchXmlDocument.documentElement.children.length != 1 || !entity || !entity.getAttribute('name')) {
29
+ result.error = {
30
+ code: '0x80041102',
31
+ message: 'Entity Name was not specified in FetchXml String.'
32
+ };
33
+ return false;
34
+ }
35
+ const invalidAttribute = fetchXmlDocument.evaluate('fetch/entity/*[not(self::filter or self::order or self::link-entity or self::attribute or self::all-attributes or self::no-attrs)]', fetchXmlDocument, null, XPathResult.ANY_TYPE, null).iterateNext();
36
+ if (invalidAttribute) {
37
+ result.error = {
38
+ code: '0x8004111c',
39
+ message: `Invalid Child Node, valid nodes are filter, order, link-entity, attribute, all-attributes, no-attrs. NodeName = ${invalidAttribute.tagName} NodeXml = ${invalidAttribute.outerHTML}`
40
+ };
41
+ return false;
42
+ }
43
+ result.fetchXml = fetchXmlDocument;
42
44
  return true;
43
45
  };
@@ -1,4 +1,6 @@
1
- import { validateNotEmpty } from './validateNotEmpty';
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ import { hasContent } from './validators/hasContent';
3
+ const option = '$orderby';
2
4
  const edmProperty = /\w{1-255}/gi;
3
5
 
4
6
  /**
@@ -6,33 +8,34 @@ const edmProperty = /\w{1-255}/gi;
6
8
  * @returns Returns `false` when the parse has an error
7
9
  */
8
10
  export const getOrderByFromParser = (parser, result) => {
9
- let $orderby = parser.get('$orderby');
10
- if ($orderby !== null) {
11
- if (!validateNotEmpty('$orderby', $orderby, result)) {
11
+ let value = parser.getAll(option);
12
+ if (value.length === 0) {
13
+ return true;
14
+ }
15
+ if (!atMostOnce(option, value, result) || !hasContent(option, value, result)) {
16
+ return false;
17
+ }
18
+ let $orderby = value[0].trimEnd();
19
+ const orderByArray = [];
20
+ for (let i = 0; i < $orderby.length; i++) {
21
+ if (false /* syntax error */) {
22
+ result.error = {
23
+ code: '0x0',
24
+ message: `Syntax error at position ${i} in '${$orderby}'.`
25
+ };
12
26
  return false;
13
27
  }
14
- $orderby = $orderby.trimEnd();
15
- const orderByArray = [];
16
- for (let i = 0; i < $orderby.length; i++) {
17
- if (false /* syntax error */) {
18
- result.error = {
19
- code: '0x0',
20
- message: `Syntax error at position ${i} in '${$orderby}'.`
21
- };
22
- return false;
23
- }
24
- }
25
- orderByArray.forEach(orderBy => {
26
- var _orderBy$column;
27
- if (!((_orderBy$column = orderBy.column) !== null && _orderBy$column !== void 0 && _orderBy$column.match(edmProperty))) {
28
- result.error = {
29
- code: '0x80060888',
30
- message: 'Order By Property must be of type EdmProperty'
31
- };
32
- return false;
33
- }
34
- });
35
- result.$orderby = orderByArray;
36
28
  }
29
+ orderByArray.forEach(orderBy => {
30
+ var _orderBy$column;
31
+ if (!((_orderBy$column = orderBy.column) !== null && _orderBy$column !== void 0 && _orderBy$column.match(edmProperty))) {
32
+ result.error = {
33
+ code: '0x80060888',
34
+ message: 'Order By Property must be of type EdmProperty'
35
+ };
36
+ return false;
37
+ }
38
+ });
39
+ result.$orderby = orderByArray;
37
40
  return true;
38
41
  };
@@ -1,11 +1,20 @@
1
+ import { atMostOnce } from './validators/atMostOnce';
2
+ const option = '$select';
3
+
1
4
  /**
2
5
  * Parses the {@link ODataSelect.$select $select} query
3
- * @returns Returns `false` when the parse has an error
6
+ * @returns {boolean} Returns `false` when the parse has an error
4
7
  */
5
8
  export const getSelectFromParser = (parser, result) => {
6
- const $select = parser.get('$select');
7
- if ($select !== null) {
8
- result.$select = $select.split(',');
9
+ const value = parser.getAll(option);
10
+ if (value.length === 0) {
11
+ return true;
12
+ }
13
+ if (!atMostOnce(option, value, result)) {
14
+ return false;
15
+ }
16
+ if (value.length > 0) {
17
+ result.$select = value[0].split(',');
9
18
  }
10
19
  return true;
11
20
  };