@shko.online/dataverse-odata 0.1.2 → 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.
- package/.eslintrc.json +59 -0
- package/.prettierrc.json +8 -0
- package/CHANGELOG.md +14 -0
- package/lib/cjs/getExpandFromParser.js +19 -12
- package/lib/cjs/getFetchXmlFromParser.js +37 -35
- package/lib/cjs/getOrderByFromParser.js +28 -25
- package/lib/cjs/getSelectFromParser.js +13 -4
- package/lib/cjs/getTopFromParser.js +25 -21
- package/lib/cjs/getXQueryFromParser.js +9 -19
- package/lib/cjs/parseOData.js +2 -18
- package/lib/cjs/validators/atMostOnce.js +24 -0
- package/lib/cjs/validators/differentFromEmptyString.js +23 -0
- package/lib/cjs/validators/hasContent.js +24 -0
- package/lib/cjs/validators/isGuid.js +25 -0
- package/lib/cjs/validators/recognizedGuid.js +23 -0
- package/lib/esm/getExpandFromParser.js +18 -12
- package/lib/esm/getFetchXmlFromParser.js +37 -35
- package/lib/esm/getOrderByFromParser.js +28 -25
- package/lib/esm/getSelectFromParser.js +13 -4
- package/lib/esm/getTopFromParser.js +24 -21
- package/lib/esm/getXQueryFromParser.js +9 -18
- package/lib/esm/parseOData.js +2 -18
- package/lib/esm/validators/atMostOnce.js +17 -0
- package/lib/esm/validators/differentFromEmptyString.js +16 -0
- package/lib/esm/validators/hasContent.js +17 -0
- package/lib/esm/validators/isGuid.js +18 -0
- package/lib/esm/validators/recognizedGuid.js +16 -0
- package/lib/modern/getExpandFromParser.js +18 -12
- package/lib/modern/getFetchXmlFromParser.js +37 -35
- package/lib/modern/getOrderByFromParser.js +29 -26
- package/lib/modern/getSelectFromParser.js +13 -4
- package/lib/modern/getTopFromParser.js +24 -21
- package/lib/modern/getXQueryFromParser.js +9 -18
- package/lib/modern/parseOData.js +2 -18
- package/lib/modern/validators/atMostOnce.js +17 -0
- package/lib/modern/validators/differentFromEmptyString.js +16 -0
- package/lib/modern/validators/hasContent.js +17 -0
- package/lib/modern/validators/isGuid.js +18 -0
- package/lib/modern/validators/recognizedGuid.js +16 -0
- package/lib/ts3.4/OData.types.d.ts +118 -0
- package/lib/ts3.4/getSelectFromParser.d.ts +1 -1
- package/lib/ts3.4/validators/atMostOnce.d.ts +10 -0
- package/lib/ts3.4/validators/differentFromEmptyString.d.ts +9 -0
- package/lib/ts3.4/validators/hasContent.d.ts +10 -0
- package/lib/ts3.4/validators/isGuid.d.ts +9 -0
- package/lib/ts3.4/validators/recognizedGuid.d.ts +9 -0
- package/lib/ts3.9/OData.types.d.ts +154 -0
- package/lib/ts3.9/getExpandFromParser.d.ts.map +1 -1
- package/lib/ts3.9/getFetchXmlFromParser.d.ts.map +1 -1
- package/lib/ts3.9/getOrderByFromParser.d.ts.map +1 -1
- package/lib/ts3.9/getSelectFromParser.d.ts +1 -1
- package/lib/ts3.9/getSelectFromParser.d.ts.map +1 -1
- package/lib/ts3.9/getTopFromParser.d.ts.map +1 -1
- package/lib/ts3.9/getXQueryFromParser.d.ts.map +1 -1
- package/lib/ts3.9/parseOData.d.ts.map +1 -1
- package/lib/ts3.9/validators/atMostOnce.d.ts +10 -0
- package/lib/ts3.9/validators/atMostOnce.d.ts.map +1 -0
- package/lib/ts3.9/validators/differentFromEmptyString.d.ts +9 -0
- package/lib/ts3.9/validators/differentFromEmptyString.d.ts.map +1 -0
- package/lib/ts3.9/validators/hasContent.d.ts +10 -0
- package/lib/ts3.9/validators/hasContent.d.ts.map +1 -0
- package/lib/ts3.9/validators/isGuid.d.ts +9 -0
- package/lib/ts3.9/validators/isGuid.d.ts.map +1 -0
- package/lib/ts3.9/validators/recognizedGuid.d.ts +9 -0
- package/lib/ts3.9/validators/recognizedGuid.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/OData.types.d.ts +33 -15
- package/src/getExpandFromParser.ts +23 -12
- package/src/getFetchXmlFromParser.ts +48 -45
- package/src/getOrderByFromParser.ts +30 -26
- package/src/getSelectFromParser.ts +13 -4
- package/src/getTopFromParser.ts +25 -21
- package/src/getXQueryFromParser.ts +10 -19
- package/src/parseOData.ts +10 -18
- package/src/validators/atMostOnce.ts +19 -0
- package/src/validators/differentFromEmptyString.ts +18 -0
- package/src/validators/hasContent.ts +19 -0
- package/src/validators/isGuid.ts +20 -0
- package/src/validators/recognizedGuid.ts +18 -0
- package/tsconfig.build.json +3 -0
- package/tsconfig.json +2 -5
- package/lib/cjs/validateNotEmpty.js +0 -17
- package/lib/esm/validateNotEmpty.js +0 -10
- package/lib/modern/validateNotEmpty.js +0 -10
- package/lib/ts3.4/validateNotEmpty.d.ts +0 -3
- package/lib/ts3.9/validateNotEmpty.d.ts +0 -3
- package/lib/ts3.9/validateNotEmpty.d.ts.map +0 -1
- package/src/validateNotEmpty.ts +0 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ODataQuery } from './OData.types';
|
|
2
2
|
/**
|
|
3
3
|
* Parses the {@link ODataSelect.$select $select} query
|
|
4
|
-
* @returns Returns `false` when the parse has an error
|
|
4
|
+
* @returns {boolean} Returns `false` when the parse has an error
|
|
5
5
|
*/
|
|
6
6
|
export declare const getSelectFromParser: (parser: URLSearchParams, result: ODataQuery) => boolean;
|
|
7
7
|
//# sourceMappingURL=getSelectFromParser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSelectFromParser.d.ts","sourceRoot":"","sources":["../../src/getSelectFromParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"getSelectFromParser.d.ts","sourceRoot":"","sources":["../../src/getSelectFromParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;AAK7D;;;GAGG;AACH,eAAO,MAAM,mBAAmB,WAAY,eAAe,UAAU,UAAU,KAAG,OAYjF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTopFromParser.d.ts","sourceRoot":"","sources":["../../src/getTopFromParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"getTopFromParser.d.ts","sourceRoot":"","sources":["../../src/getTopFromParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,eAAe,CAAC;AAM1D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,WAAY,eAAe,UAAU,UAAU,KAAG,OAwB9E,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getXQueryFromParser.d.ts","sourceRoot":"","sources":["../../src/getXQueryFromParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmC,MAAM,eAAe,CAAC;AAIjF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,MACzB,YAAY,GAAG,WAAW,UACrB,eAAe,UACf,UAAU,KACnB,
|
|
1
|
+
{"version":3,"file":"getXQueryFromParser.d.ts","sourceRoot":"","sources":["../../src/getXQueryFromParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmC,MAAM,eAAe,CAAC;AAIjF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,MACzB,YAAY,GAAG,WAAW,UACrB,eAAe,UACf,UAAU,KACnB,OAWF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseOData.d.ts","sourceRoot":"","sources":["../../src/parseOData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"parseOData.d.ts","sourceRoot":"","sources":["../../src/parseOData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAShD;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAAW,MAAM,eAavC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ODataQuery } from '../OData.types';
|
|
2
|
+
/**
|
|
3
|
+
* Options of this type must be specified at most once.
|
|
4
|
+
* @param option The option being validated (ex. $top)
|
|
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 declare const atMostOnce: (option: string, value: string[], result: ODataQuery) => boolean;
|
|
10
|
+
//# sourceMappingURL=atMostOnce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atMostOnce.d.ts","sourceRoot":"","sources":["../../../src/validators/atMostOnce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,WAAY,MAAM,SAAS,MAAM,EAAE,UAAU,UAAU,YAS7E,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ODataQuery } from '../OData.types';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
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 declare const differentFromEmptyString: (value: string[], result: ODataQuery) => boolean;
|
|
9
|
+
//# sourceMappingURL=differentFromEmptyString.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differentFromEmptyString.d.ts","sourceRoot":"","sources":["../../../src/validators/differentFromEmptyString.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,UAAW,MAAM,EAAE,UAAU,UAAU,YAS3E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ODataQuery } from '../OData.types';
|
|
2
|
+
/**
|
|
3
|
+
* Options of this type must be specified at most once.
|
|
4
|
+
* @param option The option being validated (ex. $top)
|
|
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 declare const hasContent: (query: string, value: string[], result: ODataQuery) => boolean;
|
|
10
|
+
//# sourceMappingURL=hasContent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasContent.d.ts","sourceRoot":"","sources":["../../../src/validators/hasContent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,UAAW,MAAM,SAAS,MAAM,EAAE,UAAU,UAAU,YAS5E,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ODataQuery } from '../OData.types';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
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 declare const isGuid: (value: string[], result: ODataQuery) => boolean;
|
|
9
|
+
//# sourceMappingURL=isGuid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isGuid.d.ts","sourceRoot":"","sources":["../../../src/validators/isGuid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,UAAW,MAAM,EAAE,UAAU,UAAU,YASzD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ODataQuery } from '../OData.types';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
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 declare const recognizedGuid: (value: string[], result: ODataQuery) => boolean;
|
|
9
|
+
//# sourceMappingURL=recognizedGuid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recognizedGuid.d.ts","sourceRoot":"","sources":["../../../src/validators/recognizedGuid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,UAAW,MAAM,EAAE,UAAU,UAAU,YASjE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shko.online/dataverse-odata",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "This package will help parse OData strings (only the Microsoft Dataverse subset). It can be used as a validator, or you can build some javascript library which consumes the output of this library.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run lint && node ../scripts/build.js",
|
package/src/OData.types.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
interface ODataError {
|
|
2
2
|
error?: {
|
|
3
3
|
code: string;
|
|
4
4
|
message: string;
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface ODataExpand {
|
|
9
9
|
/**
|
|
10
10
|
* Use the {@link ODataExpand.$expand $expand} system query option in the navigation properties
|
|
11
11
|
* to control what data from related entities is returned.
|
|
@@ -26,9 +26,9 @@ export interface ODataExpand {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
type ODataExpandQuery = ODataSelect & ODataExpand;
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
interface ODataFilter {
|
|
32
32
|
/**
|
|
33
33
|
* Use the {@link ODataFilter.$filter $filter} system query option to set criteria for which rows will be returned.
|
|
34
34
|
*
|
|
@@ -37,7 +37,7 @@ export interface ODataFilter {
|
|
|
37
37
|
$filter?: StandardOperator;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
interface ODataFetch {
|
|
41
41
|
/**
|
|
42
42
|
* You can compose a FetchXML query for a specific table.
|
|
43
43
|
* Then, URL-encode the XML and pass it to the entity set
|
|
@@ -48,7 +48,7 @@ export interface ODataFetch {
|
|
|
48
48
|
fetchXml?: XMLDocument;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
interface ODataOrderBy {
|
|
52
52
|
/**
|
|
53
53
|
* Specify the order in which items are returned using the {@link ODataOrderBy.$orderby $orderby}
|
|
54
54
|
* system query option. Use the asc or desc suffix to specify ascending or descending order
|
|
@@ -59,7 +59,7 @@ export interface ODataOrderBy {
|
|
|
59
59
|
$orderby?: { column: string; asc: boolean }[];
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
interface ODataSavedQuery {
|
|
63
63
|
/**
|
|
64
64
|
* You can use the `savedqueryid` value and pass it as the value to the {@link ODataSavedQuery.savedQuery savedQuery}
|
|
65
65
|
* parameter to the entity set that matches the corresponding `returnedtypecode` of the saved query.
|
|
@@ -69,7 +69,7 @@ export interface ODataSavedQuery {
|
|
|
69
69
|
savedQuery?: string;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
interface ODataSelect {
|
|
73
73
|
/**
|
|
74
74
|
* Use the {@link ODataSelect.$select $select} system query option to limit the properties returned.
|
|
75
75
|
*
|
|
@@ -81,7 +81,7 @@ export interface ODataSelect {
|
|
|
81
81
|
$select?: string[];
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
interface ODataTop {
|
|
85
85
|
/**
|
|
86
86
|
* You can limit the number of results returned by using the {@link ODataTop.$top $top} system query option.
|
|
87
87
|
*
|
|
@@ -90,7 +90,7 @@ export interface ODataTop {
|
|
|
90
90
|
$top?: number;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
interface ODataUserQuery {
|
|
94
94
|
/**
|
|
95
95
|
* You can use the `userqueryid` value and pass it as the value to the {@link OdataUserQuery.userQuery userQuery}
|
|
96
96
|
* parameter to the entity set that matches the corresponding `returnedtypecode` of the user query.
|
|
@@ -100,9 +100,9 @@ export interface ODataUserQuery {
|
|
|
100
100
|
userQuery?: string;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
type StandardOperators = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le';
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
interface StandardOperator {
|
|
106
106
|
operator: StandardOperators;
|
|
107
107
|
/**
|
|
108
108
|
* The left side of the 'X' operator must be a property of the entity.
|
|
@@ -114,18 +114,18 @@ export interface StandardOperator {
|
|
|
114
114
|
right: string | number;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
interface UnaryOperator {
|
|
118
118
|
operator: 'not';
|
|
119
119
|
right: StandardOperator;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
interface BinaryOperator {
|
|
123
123
|
operator: 'and' | 'or';
|
|
124
124
|
left: StandardOperator;
|
|
125
125
|
right: StandardOperator;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
type ODataQuery = ODataError &
|
|
129
129
|
ODataExpand &
|
|
130
130
|
ODataFetch &
|
|
131
131
|
ODataFilter &
|
|
@@ -134,3 +134,21 @@ export type ODataQuery = ODataError &
|
|
|
134
134
|
ODataSelect &
|
|
135
135
|
ODataTop &
|
|
136
136
|
ODataUserQuery;
|
|
137
|
+
|
|
138
|
+
export type {
|
|
139
|
+
BinaryOperator,
|
|
140
|
+
ODataError,
|
|
141
|
+
ODataExpand,
|
|
142
|
+
ODataExpandQuery,
|
|
143
|
+
ODataFetch,
|
|
144
|
+
ODataFilter,
|
|
145
|
+
ODataOrderBy,
|
|
146
|
+
ODataQuery,
|
|
147
|
+
ODataSavedQuery,
|
|
148
|
+
ODataSelect,
|
|
149
|
+
ODataTop,
|
|
150
|
+
ODataUserQuery,
|
|
151
|
+
StandardOperator,
|
|
152
|
+
StandardOperators,
|
|
153
|
+
UnaryOperator,
|
|
154
|
+
};
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import type { ODataError, ODataExpand, ODataExpandQuery, ODataQuery } from './OData.types';
|
|
2
2
|
|
|
3
3
|
import { getSelectFromParser } from './getSelectFromParser';
|
|
4
|
+
import { atMostOnce } from './validators/atMostOnce';
|
|
5
|
+
|
|
6
|
+
const option = '$expand';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Parses the {@link ODataExpand.$expand $expand} query
|
|
7
10
|
* @returns Returns `false` when the parse has an error
|
|
8
11
|
*/
|
|
9
12
|
export const getExpandFromParser = (parser: URLSearchParams, result: ODataQuery): boolean => {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
+
const value = parser.getAll(option);
|
|
14
|
+
if (value.length === 0) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (!atMostOnce(option, value, result)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
result.$expand = {};
|
|
22
|
+
if (!extractExpand(value[0], result)) {
|
|
23
|
+
return false;
|
|
17
24
|
}
|
|
25
|
+
|
|
18
26
|
return true;
|
|
19
27
|
};
|
|
20
28
|
|
|
21
29
|
const extractExpand = (value: string, $expand: ODataExpand & ODataError) => {
|
|
22
|
-
const match = value.match(/^\s*(\w(\w|\d|_)*)\s*(,|\()?\s*/);
|
|
30
|
+
const match = value.match(/^\s*(\w(\w|\d|_)*)\s*(,|\(|\))?\s*/);
|
|
23
31
|
if (
|
|
24
32
|
match === null ||
|
|
25
33
|
(match[0].length < value.length && match[3] === null) ||
|
|
@@ -27,7 +35,7 @@ const extractExpand = (value: string, $expand: ODataExpand & ODataError) => {
|
|
|
27
35
|
) {
|
|
28
36
|
$expand.error = {
|
|
29
37
|
code: '0x0',
|
|
30
|
-
message: '
|
|
38
|
+
message: `Term '${value}' is not valid in a $select or $expand expression.`,
|
|
31
39
|
};
|
|
32
40
|
return false;
|
|
33
41
|
}
|
|
@@ -59,7 +67,10 @@ const extractExpand = (value: string, $expand: ODataExpand & ODataError) => {
|
|
|
59
67
|
return false;
|
|
60
68
|
}
|
|
61
69
|
if (innerExpand.$expand === undefined && innerExpand.$select === undefined) {
|
|
62
|
-
$expand.error = {
|
|
70
|
+
$expand.error = {
|
|
71
|
+
code: '0x0',
|
|
72
|
+
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.`,
|
|
73
|
+
};
|
|
63
74
|
return false;
|
|
64
75
|
}
|
|
65
76
|
$expand.$expand[match[1]] = innerExpand;
|
|
@@ -90,7 +101,7 @@ const getClosingBracket = (value: string): { index: number; error?: string } =>
|
|
|
90
101
|
while (depth > 0) {
|
|
91
102
|
const match = value.substring(startAt).match(/\(|\)/);
|
|
92
103
|
if (match === null) {
|
|
93
|
-
return { error: '
|
|
104
|
+
return { error: 'Found an unbalanced bracket expression.', index: -1 };
|
|
94
105
|
}
|
|
95
106
|
if (match[0] === ')') {
|
|
96
107
|
depth -= 1;
|
|
@@ -100,7 +111,7 @@ const getClosingBracket = (value: string): { index: number; error?: string } =>
|
|
|
100
111
|
} else {
|
|
101
112
|
depth += 1;
|
|
102
113
|
}
|
|
103
|
-
startAt
|
|
114
|
+
startAt += (match.index || 0) + 1;
|
|
104
115
|
}
|
|
105
|
-
return { error: '
|
|
116
|
+
return { error: 'Found an unbalanced bracket expression.', index: -1 };
|
|
106
117
|
};
|
|
@@ -1,56 +1,59 @@
|
|
|
1
1
|
import type { ODataQuery, ODataFetch } from './OData.types';
|
|
2
|
+
import { atMostOnce } from './validators/atMostOnce';
|
|
3
|
+
import { differentFromEmptyString } from './validators/differentFromEmptyString';
|
|
4
|
+
|
|
5
|
+
const option = 'fetchXml';
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Parses the {@link ODataFetch.fetchXml fetchXml} query
|
|
5
9
|
* @returns Returns `false` when the parse has an error
|
|
6
10
|
*/
|
|
7
11
|
export const getFetchXmlFromParser = (parser: URLSearchParams, result: ODataQuery): boolean => {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
12
|
+
const value = parser.getAll(option);
|
|
13
|
+
if (value.length === 0) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (!atMostOnce(option, value, result) || !differentFromEmptyString(value, result)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const entity = fetchXmlDocument
|
|
28
|
-
.evaluate('fetch/entity', fetchXmlDocument, null, XPathResult.ANY_TYPE, null)
|
|
29
|
-
.iterateNext() as Element;
|
|
30
|
-
if (fetchXmlDocument.documentElement.children.length != 1 || !entity || !entity.getAttribute('name')) {
|
|
31
|
-
result.error = {
|
|
32
|
-
code: '0x80041102',
|
|
33
|
-
message: 'Entity Name was not specified in FetchXml String.',
|
|
34
|
-
};
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
const invalidAttribute = fetchXmlDocument
|
|
38
|
-
.evaluate(
|
|
39
|
-
'fetch/entity/*[not(self::filter or self::order or self::link-entity or self::attribute or self::all-attributes or self::no-attrs)]',
|
|
40
|
-
fetchXmlDocument,
|
|
41
|
-
null,
|
|
42
|
-
XPathResult.ANY_TYPE,
|
|
43
|
-
null,
|
|
44
|
-
)
|
|
45
|
-
.iterateNext() as Element;
|
|
46
|
-
if (invalidAttribute) {
|
|
47
|
-
result.error = {
|
|
48
|
-
code: '0x8004111c',
|
|
49
|
-
message: `Invalid Child Node, valid nodes are filter, order, link-entity, attribute, all-attributes, no-attrs. NodeName = ${invalidAttribute.tagName} NodeXml = ${invalidAttribute.outerHTML}`,
|
|
50
|
-
};
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
result.fetchXml = fetchXmlDocument;
|
|
20
|
+
const fetchXml = value[0];
|
|
21
|
+
const serializer = new DOMParser();
|
|
22
|
+
const fetchXmlDocument = serializer.parseFromString(fetchXml, 'text/xml');
|
|
23
|
+
if (fetchXmlDocument.documentElement.tagName === 'parsererror') {
|
|
24
|
+
result.error = {
|
|
25
|
+
code: '0x80040201',
|
|
26
|
+
message: 'Invalid XML.',
|
|
27
|
+
};
|
|
28
|
+
return false;
|
|
54
29
|
}
|
|
30
|
+
const entity = fetchXmlDocument
|
|
31
|
+
.evaluate('fetch/entity', fetchXmlDocument, null, XPathResult.ANY_TYPE, null)
|
|
32
|
+
.iterateNext() as Element;
|
|
33
|
+
if (fetchXmlDocument.documentElement.children.length != 1 || !entity || !entity.getAttribute('name')) {
|
|
34
|
+
result.error = {
|
|
35
|
+
code: '0x80041102',
|
|
36
|
+
message: 'Entity Name was not specified in FetchXml String.',
|
|
37
|
+
};
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const invalidAttribute = fetchXmlDocument
|
|
41
|
+
.evaluate(
|
|
42
|
+
'fetch/entity/*[not(self::filter or self::order or self::link-entity or self::attribute or self::all-attributes or self::no-attrs)]',
|
|
43
|
+
fetchXmlDocument,
|
|
44
|
+
null,
|
|
45
|
+
XPathResult.ANY_TYPE,
|
|
46
|
+
null,
|
|
47
|
+
)
|
|
48
|
+
.iterateNext() as Element;
|
|
49
|
+
if (invalidAttribute) {
|
|
50
|
+
result.error = {
|
|
51
|
+
code: '0x8004111c',
|
|
52
|
+
message: `Invalid Child Node, valid nodes are filter, order, link-entity, attribute, all-attributes, no-attrs. NodeName = ${invalidAttribute.tagName} NodeXml = ${invalidAttribute.outerHTML}`,
|
|
53
|
+
};
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
result.fetchXml = fetchXmlDocument;
|
|
57
|
+
|
|
55
58
|
return true;
|
|
56
59
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ODataQuery, ODataOrderBy } from './OData.types';
|
|
2
|
-
import {
|
|
2
|
+
import { atMostOnce } from './validators/atMostOnce';
|
|
3
|
+
import { hasContent } from './validators/hasContent';
|
|
3
4
|
|
|
5
|
+
const option = '$orderby';
|
|
4
6
|
const edmProperty = /\w{1-255}/gi;
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -8,35 +10,37 @@ const edmProperty = /\w{1-255}/gi;
|
|
|
8
10
|
* @returns Returns `false` when the parse has an error
|
|
9
11
|
*/
|
|
10
12
|
export const getOrderByFromParser = (parser: URLSearchParams, result: ODataQuery): boolean => {
|
|
11
|
-
let
|
|
12
|
-
if (
|
|
13
|
-
|
|
13
|
+
let value = parser.getAll(option);
|
|
14
|
+
if (value.length === 0) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (!atMostOnce(option, value, result) || !hasContent(option, value, result)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
let $orderby = value[0].trimEnd();
|
|
21
|
+
const orderByArray: ODataOrderBy['$orderby'] = [];
|
|
22
|
+
for (let i = 0; i < $orderby.length; i++) {
|
|
23
|
+
if (false /* syntax error */) {
|
|
24
|
+
result.error = {
|
|
25
|
+
code: '0x0',
|
|
26
|
+
message: `Syntax error at position ${i} in '${$orderby}'.`,
|
|
27
|
+
};
|
|
28
|
+
|
|
14
29
|
return false;
|
|
15
30
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
orderByArray.forEach((orderBy) => {
|
|
34
|
+
if (!orderBy.column?.match(edmProperty)) {
|
|
35
|
+
result.error = {
|
|
36
|
+
code: '0x80060888',
|
|
37
|
+
message: 'Order By Property must be of type EdmProperty',
|
|
38
|
+
};
|
|
39
|
+
return false;
|
|
27
40
|
}
|
|
41
|
+
});
|
|
28
42
|
|
|
29
|
-
|
|
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
|
-
});
|
|
43
|
+
result.$orderby = orderByArray;
|
|
38
44
|
|
|
39
|
-
result.$orderby = orderByArray;
|
|
40
|
-
}
|
|
41
45
|
return true;
|
|
42
46
|
};
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type { ODataQuery, ODataSelect } from './OData.types';
|
|
2
|
+
import { atMostOnce } from './validators/atMostOnce';
|
|
3
|
+
|
|
4
|
+
const option = '$select';
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Parses the {@link ODataSelect.$select $select} query
|
|
5
|
-
* @returns Returns `false` when the parse has an error
|
|
8
|
+
* @returns {boolean} Returns `false` when the parse has an error
|
|
6
9
|
*/
|
|
7
10
|
export const getSelectFromParser = (parser: URLSearchParams, result: ODataQuery): boolean => {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
+
const value = parser.getAll(option);
|
|
12
|
+
if (value.length === 0) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (!atMostOnce(option, value, result)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (value.length > 0) {
|
|
19
|
+
result.$select = value[0].split(',');
|
|
11
20
|
}
|
|
12
21
|
return true;
|
|
13
22
|
};
|
package/src/getTopFromParser.ts
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import type { ODataQuery, ODataTop } from './OData.types';
|
|
2
|
-
import {
|
|
2
|
+
import { atMostOnce } from './validators/atMostOnce';
|
|
3
|
+
import { hasContent } from './validators/hasContent';
|
|
4
|
+
|
|
5
|
+
const option = '$top';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Parses the {@link ODataTop.$top $top} query
|
|
6
9
|
* @returns Returns `false` when the parse has an error
|
|
7
10
|
*/
|
|
8
11
|
export const getTopFromParser = (parser: URLSearchParams, result: ODataQuery): boolean => {
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
12
|
+
const value = parser.getAll(option);
|
|
13
|
+
if (value.length === 0) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (!atMostOnce(option, value, result) || !hasContent(option, value, result)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
let $top: number;
|
|
20
|
+
if (!value[0].match(/^\d+$/) || ($top = parseInt(value[0])) < 0) {
|
|
21
|
+
result.error = {
|
|
22
|
+
code: '0x0',
|
|
23
|
+
message: `Invalid value '${value}' for $top query option found. The $top query option requires a non-negative integer value.`,
|
|
24
|
+
};
|
|
25
|
+
return false;
|
|
26
|
+
} else if ($top === 0) {
|
|
27
|
+
result.error = {
|
|
28
|
+
code: '0x0',
|
|
29
|
+
message: `Invalid value for $top query option.`,
|
|
30
|
+
};
|
|
31
|
+
return false;
|
|
29
32
|
}
|
|
33
|
+
result.$top = $top;
|
|
30
34
|
return true;
|
|
31
35
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ODataQuery, ODataSavedQuery, ODataUserQuery } from './OData.types';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { isGuid } from './validators/isGuid';
|
|
3
|
+
import { recognizedGuid } from './validators/recognizedGuid';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Parses the {@link ODataSavedQuery.savedQuery savedQuery} or
|
|
@@ -12,23 +12,14 @@ export const getXQueryFromParser = (
|
|
|
12
12
|
parser: URLSearchParams,
|
|
13
13
|
result: ODataQuery,
|
|
14
14
|
): boolean => {
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
if (!xQuery.match(guidRegex)) {
|
|
25
|
-
result.error = {
|
|
26
|
-
code: '0x0',
|
|
27
|
-
message: 'Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).',
|
|
28
|
-
};
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
result[X] = xQuery;
|
|
15
|
+
const value = parser.getAll(X);
|
|
16
|
+
if (value.length === 0) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (!recognizedGuid(value, result) || !isGuid(value, result)) {
|
|
20
|
+
return false;
|
|
32
21
|
}
|
|
22
|
+
|
|
23
|
+
result[X] = value[0];
|
|
33
24
|
return true;
|
|
34
25
|
};
|