Skip to content
This repository was archived by the owner on Apr 11, 2023. It is now read-only.

Commit 1f175a4

Browse files
committed
ADD: ODataV4 $expand support with nested operators; remove resctrictions from OData include tests
1 parent 5670125 commit 1f175a4

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import $data, { $C, Guard, Container, Exception, MemberDefinition } from 'jaydata/core';
2+
3+
export class ODataIncludeFragment {
4+
constructor (name) {
5+
this.name = name;
6+
this.$expand = [];
7+
this.$operators = [];
8+
}
9+
10+
toString(){
11+
let data = '';
12+
if(this.$expand.length){
13+
if(this.name){
14+
data += this.name + '($expand=';
15+
}
16+
for(let i = 0; i < this.$expand.length; i++){
17+
if(i !== 0) data += ',';
18+
data += this[this.$expand[i]].toString();
19+
}
20+
if(this.name) {
21+
data += ')';
22+
}
23+
}
24+
25+
if(this.name){
26+
for (let i = 0; i < this.$operators.length; i++) {
27+
let operator = this.$operators[i];
28+
let values = this[operator];
29+
for(let j = 0; j < values.length; j++){
30+
if(data) data += ','
31+
data += this.name + '(' + operator + '='
32+
data += values[j];
33+
data += ')';
34+
}
35+
}
36+
}
37+
38+
if(this.name && !data) {
39+
data = this.name;
40+
}
41+
42+
return data;
43+
}
44+
45+
addInclude(path, map){
46+
this._createIncludePath(path);
47+
}
48+
49+
addImplicitMap(path, map){
50+
var includedFragment = this._createIncludePath(path);
51+
this._setImplicitMap(includedFragment, map);
52+
}
53+
54+
_createIncludePath(path) {
55+
if(!path) return this;
56+
var inc = path.split('.');
57+
58+
var current = this;
59+
for(var i = 0; i < inc.length; i++){
60+
var it = inc[i];
61+
var included = true;
62+
if(current.$expand.indexOf(it) < 0){
63+
included = false;
64+
current.$expand.push(it);
65+
current[it] = new ODataIncludeFragment(it);
66+
current[it].__implicit = true;
67+
}
68+
69+
current = current[it];
70+
if(i < inc.length - 1 && current.__implicit){
71+
this._setImplicitMap(current, inc[i+1]);
72+
}
73+
}
74+
75+
return current;
76+
}
77+
_setImplicitMap(includeFragment, map){
78+
if(map){
79+
if (includeFragment.$operators.indexOf('$select') < 0) {
80+
if(includeFragment.__implicit) {
81+
includeFragment.$operators.push('$select');
82+
includeFragment.$select = [map];
83+
}
84+
} else if(includeFragment.$expand.indexOf(map) < 0) {
85+
includeFragment.$select[0] += ',' + map;
86+
}
87+
}
88+
}
89+
}
90+
91+
$data.storageProviders.oData.ODataIncludeFragment = ODataIncludeFragment;
92+
93+
$C('$data.storageProviders.oData.oDataIncludeCompiler', $data.Expressions.EntityExpressionVisitor, null, {
94+
constructor: function (provider) {
95+
this.provider = provider;
96+
},
97+
98+
compile: function (expression, context) {
99+
context.data = context.data || new ODataIncludeFragment();
100+
context.current = context.data;
101+
this.Visit(expression, context);
102+
103+
},
104+
VisitParametricQueryExpression: function (expression, context) {
105+
this.Visit(expression.expression, context);
106+
},
107+
108+
VisitEntitySetExpression: function (expression, context) {
109+
this.Visit(expression.source, context);
110+
if (expression.selector instanceof $data.Expressions.AssociationInfoExpression) {
111+
this.Visit(expression.selector, context);
112+
}
113+
},
114+
115+
VisitAssociationInfoExpression: function (expression, context) {
116+
var propName = expression.associationInfo.FromPropertyName;
117+
118+
this.includePath = this.includePath ? (this.includePath + '.') : "";
119+
this.includePath += propName;
120+
121+
var currentPath = this.includePath;
122+
if (!context.includes.some(function (include) { return include.name == currentPath }, this)) {
123+
context.includes.push({ name: currentPath, type: expression.associationInfo.ToType });
124+
}
125+
126+
if(context.current.$expand.indexOf(propName) < 0)
127+
{
128+
context.current.$expand.push(propName);
129+
context.current[propName] = new ODataIncludeFragment(propName);
130+
}
131+
context.current = context.current[propName];
132+
},
133+
134+
VisitFrameOperationExpression: function (expression, context) {
135+
this.Visit(expression.source, context);
136+
137+
var opDef = expression.operation.memberDefinition;
138+
if(opDef && opDef.includeFrameName){
139+
var opName = opDef.includeFrameName;
140+
var paramCounter = 0;
141+
var params = opDef.parameters || [{ name: "@expression" }];
142+
143+
var args = params.map(function (item, index) {
144+
if (item.name === "@expression") {
145+
return expression.source;
146+
} else {
147+
return expression.parameters[paramCounter++]
148+
};
149+
});
150+
151+
if(opDef.includeCompiler){
152+
for (var i = 0; i < args.length; i++) {
153+
var arg = args[i];
154+
if (arg && arg.value instanceof $data.Queryable) {
155+
var preparator = Container.createQueryExpressionCreator(arg.value.entityContext);
156+
var prep_expression = preparator.Visit(arg.value.expression);
157+
158+
var compilerType = Container.resolveType(opDef.includeCompiler);
159+
var compiler = new compilerType(this.provider);
160+
var frameContext = { data: "", $expand: context.current };
161+
var compiled = compiler.compile(prep_expression, frameContext);
162+
163+
if(context.current['$operators'].indexOf(opName) < 0){
164+
context.current[opName] = [];
165+
context.current['$operators'].push(opName);
166+
}
167+
context.current[opName].push(frameContext[opName] || frameContext.data);
168+
};
169+
}
170+
} else if(opDef.implementation) {
171+
if(context.current['$operators'].indexOf(opName) < 0){
172+
context.current[opName] = [];
173+
context.current['$operators'].push(opName);
174+
}
175+
context.current[opName].push(opDef.implementation());
176+
}
177+
}
178+
}
179+
});

0 commit comments

Comments
 (0)