@sap/cds-compiler 4.4.4 → 4.6.0
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/CHANGELOG.md +88 -0
- package/bin/cdsc.js +18 -11
- package/bin/cdsv2m.js +7 -5
- package/doc/CHANGELOG_BETA.md +22 -0
- package/lib/api/main.js +306 -144
- package/lib/api/options.js +18 -6
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +45 -10
- package/lib/base/messages.js +33 -16
- package/lib/base/model.js +4 -0
- package/lib/base/optionProcessorHelper.js +45 -176
- package/lib/checks/annotationsOData.js +49 -0
- package/lib/checks/elements.js +32 -34
- package/lib/checks/enricher.js +39 -3
- package/lib/checks/validator.js +8 -7
- package/lib/compiler/assert-consistency.js +40 -17
- package/lib/compiler/builtins.js +30 -53
- package/lib/compiler/checks.js +46 -14
- package/lib/compiler/cycle-detector.js +1 -4
- package/lib/compiler/define.js +35 -10
- package/lib/compiler/extend.js +21 -7
- package/lib/compiler/generate.js +3 -0
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +46 -9
- package/lib/compiler/resolve.js +94 -35
- package/lib/compiler/shared.js +60 -33
- package/lib/compiler/tweak-assocs.js +188 -92
- package/lib/compiler/utils.js +11 -1
- package/lib/edm/annotations/edmJson.js +41 -66
- package/lib/edm/annotations/genericTranslation.js +27 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -3
- package/lib/edm/csn2edm.js +28 -11
- package/lib/edm/edmInboundChecks.js +58 -15
- package/lib/edm/edmPreprocessor.js +12 -16
- package/lib/edm/edmUtils.js +5 -2
- package/lib/gen/Dictionary.json +10 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +15 -2
- package/lib/gen/language.tokens +1 -0
- package/lib/gen/languageParser.js +6557 -5618
- package/lib/json/from-csn.js +4 -5
- package/lib/json/to-csn.js +29 -4
- package/lib/language/antlrParser.js +19 -1
- package/lib/language/errorStrategy.js +28 -7
- package/lib/language/genericAntlrParser.js +118 -24
- package/lib/language/textUtils.js +16 -0
- package/lib/main.d.ts +28 -3
- package/lib/main.js +3 -0
- package/lib/model/csnRefs.js +4 -1
- package/lib/model/csnUtils.js +20 -14
- package/lib/model/revealInternalProperties.js +5 -2
- package/lib/optionProcessor.js +23 -22
- package/lib/render/manageConstraints.js +13 -29
- package/lib/render/toCdl.js +47 -26
- package/lib/render/toHdbcds.js +63 -42
- package/lib/render/toRename.js +6 -10
- package/lib/render/toSql.js +71 -117
- package/lib/render/utils/common.js +41 -6
- package/lib/transform/.eslintrc.json +9 -1
- package/lib/transform/addTenantFields.js +228 -0
- package/lib/transform/db/applyTransformations.js +57 -4
- package/lib/transform/db/assertUnique.js +4 -4
- package/lib/transform/db/backlinks.js +13 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +24 -3
- package/lib/transform/db/flattening.js +70 -71
- package/lib/transform/db/killAnnotations.js +37 -0
- package/lib/transform/db/rewriteCalculatedElements.js +46 -6
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/draft/db.js +2 -16
- package/lib/transform/draft/odata.js +3 -3
- package/lib/transform/effective/associations.js +3 -5
- package/lib/transform/effective/main.js +6 -9
- package/lib/transform/forOdata.js +26 -55
- package/lib/transform/forRelationalDB.js +38 -18
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/transform/transformUtils.js +47 -34
- package/lib/transform/translateAssocsToJoins.js +45 -11
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +7 -6
|
@@ -7,8 +7,8 @@ const {
|
|
|
7
7
|
EdmTypeFacetNames,
|
|
8
8
|
EdmPrimitiveTypeMap,
|
|
9
9
|
} = require('../EdmPrimitiveTypeDefinitions.js');
|
|
10
|
-
const {
|
|
11
|
-
const {
|
|
10
|
+
const { isBuiltinType, isAnnotationExpression } = require('../../model/csnUtils');
|
|
11
|
+
const { transformExpression } = require('../../transform/db/applyTransformations.js');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Translate a given token stream expression into an edmJson representation
|
|
@@ -77,9 +77,9 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
77
77
|
};
|
|
78
78
|
//----------------------------------
|
|
79
79
|
// Error transformer
|
|
80
|
-
const notADynExpr = (parent, op, xpr, parentparent, parentprop, txt) => {
|
|
80
|
+
const notADynExpr = (parent, op, xpr, csnPath, parentparent, parentprop, txt) => {
|
|
81
81
|
error('odata-anno-xpr', location, {
|
|
82
|
-
anno, op: txt
|
|
82
|
+
anno, op: txt ?? op, '#': 'notadynexpr',
|
|
83
83
|
});
|
|
84
84
|
delete parent[op];
|
|
85
85
|
};
|
|
@@ -93,27 +93,27 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
93
93
|
//----------------------------------
|
|
94
94
|
// operators not supported as dynamic expression
|
|
95
95
|
'.': notADynExpr,
|
|
96
|
-
isNull: (p, o) => notADynExpr(p, o, null, null, null, 'is null'),
|
|
97
|
-
isNotNull: (p, o) => notADynExpr(p, o, null, null, null, 'is not null'),
|
|
96
|
+
isNull: (p, o) => notADynExpr(p, o, null, null, null, null, 'is null'),
|
|
97
|
+
isNotNull: (p, o) => notADynExpr(p, o, null, null, null, null, 'is not null'),
|
|
98
98
|
exists: notADynExpr,
|
|
99
99
|
'#': notADynExpr,
|
|
100
100
|
SELECT: notADynExpr,
|
|
101
|
-
SET: (p, o) => notADynExpr(p, o, null, null, null, 'UNION'),
|
|
101
|
+
SET: (p, o) => notADynExpr(p, o, null, null, null, null, 'UNION'),
|
|
102
102
|
like: notADynExpr,
|
|
103
103
|
new: notADynExpr,
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
//----------------------------------
|
|
107
107
|
// list is a $Collection => []
|
|
108
|
-
transform.list = (parent, prop, xpr, parentparent, parentprop) => {
|
|
108
|
+
transform.list = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
109
109
|
parentparent[parentprop] = xpr.filter(a => a);
|
|
110
|
-
|
|
110
|
+
transformExpression(parentparent, parentprop, transform);
|
|
111
111
|
};
|
|
112
112
|
// XPR
|
|
113
|
-
transform.xpr = (parent, prop, xpr, parentparent, parentprop) => {
|
|
113
|
+
transform.xpr = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
114
114
|
// eliminate 'xpr' node by pulling up xpr node to its parent
|
|
115
115
|
parentparent[parentprop] = xpr;
|
|
116
|
-
|
|
116
|
+
transformExpression(parentparent, parentprop, transform);
|
|
117
117
|
};
|
|
118
118
|
//----------------------------------
|
|
119
119
|
// CASE
|
|
@@ -144,12 +144,14 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
144
144
|
curIf.$If.push(caseExpr[i]);
|
|
145
145
|
parent.$If = edmIf.$If;
|
|
146
146
|
delete parent.case;
|
|
147
|
-
|
|
147
|
+
transformExpression(parent, undefined, transform);
|
|
148
|
+
};
|
|
149
|
+
transform.$If = (_parent, _prop, expr) => {
|
|
150
|
+
transformExpression(expr, undefined, transform);
|
|
148
151
|
};
|
|
149
|
-
transform.$If = noOp;
|
|
150
152
|
//----------------------------------
|
|
151
153
|
// Cast => $Cast
|
|
152
|
-
transform.cast = (parent, prop, castExpr, parentparent, parentprop) => {
|
|
154
|
+
transform.cast = (parent, prop, castExpr, csnPath, parentparent, parentprop) => {
|
|
153
155
|
const csnType = castExpr[0];
|
|
154
156
|
// try to resolve to final scalar base type and use that instead of derived type
|
|
155
157
|
if (!isBuiltinType(csnType.type)) {
|
|
@@ -161,7 +163,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
161
163
|
});
|
|
162
164
|
}
|
|
163
165
|
}
|
|
164
|
-
const edmTypeName = mapCdsToEdmType(csnType, messageFunctions, options.isV2(), false, location);
|
|
166
|
+
const edmTypeName = edmUtils.mapCdsToEdmType(csnType, messageFunctions, options.isV2(), false, location);
|
|
165
167
|
const typeFunc = { func: 'Type', args: [ { val: edmTypeName } ] };
|
|
166
168
|
const castFunc = { func: '$Cast', args: [ typeFunc, castExpr[1] ] };
|
|
167
169
|
|
|
@@ -184,7 +186,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
184
186
|
|
|
185
187
|
|
|
186
188
|
parentparent[parentprop] = castFunc;
|
|
187
|
-
|
|
189
|
+
transformExpression(parentparent, parentprop, transform);
|
|
188
190
|
};
|
|
189
191
|
//----------------------------------
|
|
190
192
|
const evalArgs = (argDef, args, propName) => {
|
|
@@ -220,7 +222,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
220
222
|
evalArgs({ exact }, xpr, prop);
|
|
221
223
|
parent[opStr] = xpr;
|
|
222
224
|
delete parent[prop];
|
|
223
|
-
|
|
225
|
+
transformExpression(parent, undefined, transform);
|
|
224
226
|
};
|
|
225
227
|
//----------------------------------
|
|
226
228
|
// LOGICAL
|
|
@@ -249,12 +251,12 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
249
251
|
evalArgs({ min: 1 }, xpr[1].list, prop);
|
|
250
252
|
parent.$In = [ xpr[0], ...xpr[1].list ];
|
|
251
253
|
delete parent[prop];
|
|
252
|
-
|
|
254
|
+
transformExpression(parent, undefined, transform);
|
|
253
255
|
};
|
|
254
256
|
transform.$In = noOp;
|
|
255
|
-
transform.between = (parent, prop, xpr, parentparent, parentprop) => {
|
|
257
|
+
transform.between = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
256
258
|
evalArgs({ exact: 2 }, xpr.slice(1), prop);
|
|
257
|
-
|
|
259
|
+
transformExpression(xpr, undefined, transform);
|
|
258
260
|
delete parent[prop];
|
|
259
261
|
parentparent[parentprop]
|
|
260
262
|
= {
|
|
@@ -264,22 +266,22 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
264
266
|
],
|
|
265
267
|
};
|
|
266
268
|
};
|
|
267
|
-
transform['||'] = (parent, prop, xpr, parentparent, parentprop) => {
|
|
269
|
+
transform['||'] = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
268
270
|
evalArgs({ exact: 2 }, xpr, prop);
|
|
269
|
-
|
|
271
|
+
transformExpression(xpr, undefined, transform);
|
|
270
272
|
delete parent[prop];
|
|
271
273
|
parentparent[parentprop].$Apply = [ { $Function: 'odata.concat' }, ...xpr ];
|
|
272
274
|
};
|
|
273
275
|
//----------------------------------
|
|
274
276
|
// ARITHMETICAL AND UNARY
|
|
275
|
-
transform['+'] = (parent, prop, xpr, parentparent, parentprop) => {
|
|
277
|
+
transform['+'] = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
276
278
|
if (Array.isArray(xpr)) {
|
|
277
279
|
op('$Add')(parent, prop, xpr);
|
|
278
280
|
}
|
|
279
281
|
else {
|
|
280
282
|
delete parent[prop];
|
|
281
283
|
parentparent[parentprop] = xpr;
|
|
282
|
-
|
|
284
|
+
transformExpression(parentparent, parentprop, transform);
|
|
283
285
|
}
|
|
284
286
|
};
|
|
285
287
|
transform.$Add = noOp;
|
|
@@ -295,13 +297,13 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
295
297
|
// $DivBy, $Mod are functions
|
|
296
298
|
//----------------------------------
|
|
297
299
|
// LITERALS
|
|
298
|
-
transform.val = (parent, prop, xpr, parentparent, parentprop) => {
|
|
300
|
+
transform.val = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
299
301
|
if (xpr === null)
|
|
300
302
|
parent.$Null = true;
|
|
301
303
|
else
|
|
302
304
|
parentparent[parentprop] = xpr;
|
|
303
305
|
};
|
|
304
|
-
transform.ref = (parent, prop, xpr, parentparent, parentprop) => {
|
|
306
|
+
transform.ref = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
305
307
|
if (xpr.some(ps => ps.args || ps.where)) {
|
|
306
308
|
error('odata-anno-xpr-args', location, {
|
|
307
309
|
anno, elemref: parent, '#': 'wrongref',
|
|
@@ -311,7 +313,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
311
313
|
};
|
|
312
314
|
//----------------------------------
|
|
313
315
|
// Functions
|
|
314
|
-
transform.func = (parent, prop, xpr, parentparent, parentprop) => {
|
|
316
|
+
transform.func = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
315
317
|
const rewriteArgs = (argDefs, evalVal = true) => {
|
|
316
318
|
Object.entries(argDefs).forEach(([ argName, argDef ]) => {
|
|
317
319
|
const [ foundProps, newArgs ]
|
|
@@ -590,7 +592,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
590
592
|
delete parent.args;
|
|
591
593
|
};
|
|
592
594
|
|
|
593
|
-
const exactArgs = (tgt = parent, x = xpr, count) => {
|
|
595
|
+
const exactArgs = (tgt = parent, x = xpr, count = undefined) => {
|
|
594
596
|
standard(tgt, x);
|
|
595
597
|
evalArgs({ exact: count }, tgt[x], xpr);
|
|
596
598
|
};
|
|
@@ -694,7 +696,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
694
696
|
// $Record ???
|
|
695
697
|
$Collection: () => {
|
|
696
698
|
standard(parentparent, parentprop);
|
|
697
|
-
|
|
699
|
+
transformExpression(parentparent, parentprop, transform);
|
|
698
700
|
},
|
|
699
701
|
$Path: () => {
|
|
700
702
|
oneArg(parent, xpr);
|
|
@@ -704,7 +706,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
704
706
|
anno, op: `${xpr}(…)`, meta: 'string', '#': 'wrongval_meta',
|
|
705
707
|
});
|
|
706
708
|
}
|
|
707
|
-
|
|
709
|
+
transformExpression(parentparent, parentprop, transform);
|
|
708
710
|
},
|
|
709
711
|
$Null: () => {
|
|
710
712
|
parent[xpr] = true;
|
|
@@ -724,7 +726,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
724
726
|
funcDef.forEach(f => f());
|
|
725
727
|
else
|
|
726
728
|
funcDef();
|
|
727
|
-
|
|
729
|
+
transformExpression(parent, undefined, transform);
|
|
728
730
|
}
|
|
729
731
|
else {
|
|
730
732
|
const funcName = xpr.startsWith('odata.') ? xpr : `odata.${xpr}`;
|
|
@@ -740,7 +742,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
740
742
|
parentparent[parentprop].$Apply = [ { $Function: funcName }, ...(parent.args || []) ];
|
|
741
743
|
delete parentparent[parentprop].func;
|
|
742
744
|
delete parentparent[parentprop].args;
|
|
743
|
-
|
|
745
|
+
transformExpression(parentparent, parentprop, transform);
|
|
744
746
|
}
|
|
745
747
|
}
|
|
746
748
|
else {
|
|
@@ -752,41 +754,14 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
752
754
|
delete parent[prop];
|
|
753
755
|
};
|
|
754
756
|
|
|
755
|
-
return
|
|
756
|
-
'=': (parent, prop, xpr, parentparent, parentprop) => {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}).annoVal;
|
|
761
|
-
|
|
762
|
-
function applyTransformations( parent, customTransformers ) {
|
|
763
|
-
function standard( _parent, _prop, node ) {
|
|
764
|
-
if (!node || typeof node !== 'object' ||
|
|
765
|
-
!{}.propertyIsEnumerable.call( _parent, _prop ) ||
|
|
766
|
-
(typeof _prop === 'string' && _prop.startsWith('@')))
|
|
767
|
-
return;
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (Array.isArray(node)) {
|
|
771
|
-
node.forEach( (n, i) => standard( node, i, n ) );
|
|
757
|
+
return transformExpression(carrier, anno, {
|
|
758
|
+
'=': (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
759
|
+
if (isAnnotationExpression(parent)) {
|
|
760
|
+
delete parent['='];
|
|
761
|
+
parentparent[parentprop] = transformExpression({ $edmJson: parseExpr( parent, { array: false }) }, undefined, transform);
|
|
772
762
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
const ct = customTransformers[name];
|
|
776
|
-
if (ct) {
|
|
777
|
-
if (Array.isArray(ct))
|
|
778
|
-
ct.forEach(cti => cti(node, name, node[name], _parent, _prop));
|
|
779
|
-
else
|
|
780
|
-
ct(node, name, node[name], _parent, _prop);
|
|
781
|
-
}
|
|
782
|
-
standard(node, name, node[name]);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
for (const name of Object.getOwnPropertyNames( parent ))
|
|
787
|
-
standard( parent, name, parent[name] );
|
|
788
|
-
return parent;
|
|
789
|
-
}
|
|
763
|
+
},
|
|
764
|
+
});
|
|
790
765
|
}
|
|
791
766
|
|
|
792
767
|
// Not everything that can occur in OData annotations can be expressed with
|
|
@@ -302,10 +302,10 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
302
302
|
if (knownAnnos.length === 0)
|
|
303
303
|
return;
|
|
304
304
|
}
|
|
305
|
-
if (isBetaEnabled(options, '
|
|
305
|
+
if (isBetaEnabled(options, 'odataAnnotationExpressions')) {
|
|
306
306
|
knownAnnos.forEach((knownAnno) => {
|
|
307
307
|
if (knownAnno.search(/\.\$edmJson\./g) < 0)
|
|
308
|
-
|
|
308
|
+
xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
|
|
309
309
|
});
|
|
310
310
|
}
|
|
311
311
|
|
|
@@ -676,7 +676,7 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
676
676
|
|
|
677
677
|
// anno is the full <Annotation Term=...>
|
|
678
678
|
const anno = handleTerm(fullTermName, prefixTree[voc][term], msg);
|
|
679
|
-
if (anno
|
|
679
|
+
if (!anno?.$isInvalid) {
|
|
680
680
|
// addAnnotationFunc needs AppliesTo message from dictionary to decide where to put the anno
|
|
681
681
|
const termName = fullTermName.replace(/#(\w+)$/g, ''); // remove qualifier
|
|
682
682
|
const dictTerm = getDictTerm(termName, msg); // message for unknown term was already issued in handleTerm
|
|
@@ -835,14 +835,16 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
835
835
|
}
|
|
836
836
|
else if ( Object.keys(cAnnoValue).filter( x => x[0] !== '@' ).length === 0) {
|
|
837
837
|
// object consists only of properties starting with "@", no $value
|
|
838
|
+
setProp(oTarget, '$isInvalid', true);
|
|
838
839
|
message('odata-anno-value', msg.location,
|
|
839
840
|
{ anno: msg.anno(), str: 'base', '#': 'nested' } );
|
|
840
841
|
}
|
|
841
842
|
else {
|
|
842
843
|
// regular record
|
|
843
844
|
if (dTypeIsACollection) {
|
|
844
|
-
message('odata-anno-value', msg.location,
|
|
845
|
-
|
|
845
|
+
message('odata-anno-value', msg.location, {
|
|
846
|
+
anno: msg.anno(), str: 'structured', type: dTypeName, '#': 'incompval',
|
|
847
|
+
});
|
|
846
848
|
}
|
|
847
849
|
oTarget.append(generateRecord(cAnnoValue, oTermName, dTypeName, dTypeIsACollection, msg));
|
|
848
850
|
}
|
|
@@ -1100,8 +1102,15 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
1100
1102
|
}
|
|
1101
1103
|
}
|
|
1102
1104
|
|
|
1103
|
-
if ( [
|
|
1104
|
-
resolvedType
|
|
1105
|
+
if ( EdmPathTypeMap[resolvedType] ) {
|
|
1106
|
+
if (resolvedType === 'Edm.AnyPropertyPath') {
|
|
1107
|
+
resolvedType = 'PropertyPath';
|
|
1108
|
+
typeName = resolvedType;
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
resolvedType = resolvedType.split('.')[1];
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1105
1114
|
|
|
1106
1115
|
return {
|
|
1107
1116
|
name: typeName,
|
|
@@ -1139,8 +1148,17 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
1139
1148
|
actualTypeName = obj.$Type;
|
|
1140
1149
|
if (!getDictType(actualTypeName)) {
|
|
1141
1150
|
// this type doesn't exist
|
|
1142
|
-
|
|
1143
|
-
|
|
1151
|
+
if (typeof actualTypeName !== 'string') {
|
|
1152
|
+
actualTypeName = JSON.stringify(obj.$Type);
|
|
1153
|
+
message('odata-anno-type', msg.location,
|
|
1154
|
+
{
|
|
1155
|
+
anno: msg.anno(), code: '$Type', rawvalue: actualTypeName, '#': 'literal',
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
message('odata-anno-type', msg.location,
|
|
1160
|
+
{ anno: msg.anno(), type: actualTypeName, '#': 'unknown' });
|
|
1161
|
+
}
|
|
1144
1162
|
// explicitly mentioned type, render in XML and JSON
|
|
1145
1163
|
newRecord.setXml({ Type: actualTypeName });
|
|
1146
1164
|
// unknown dictionary type: can't fully qualify it
|
|
@@ -40,11 +40,10 @@ function preprocessAnnotations( csn, serviceName, options ) {
|
|
|
40
40
|
const keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
|
|
41
41
|
if (keyNames.length === 0) {
|
|
42
42
|
keyNames.push('MISSING');
|
|
43
|
-
message('odata-anno-preproc',
|
|
44
|
-
'target $(NAME) has no key');
|
|
43
|
+
message('odata-anno-preproc', assoc.$path, { anno, name: targetName, '#': 'nokey' } );
|
|
45
44
|
}
|
|
46
45
|
else if (keyNames.length > 1) {
|
|
47
|
-
message('odata-anno-preproc',
|
|
46
|
+
message('odata-anno-preproc', assoc.$path, { anno, name: targetName, '#': 'multkeys' });
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
return keyNames[0];
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -14,7 +14,6 @@ const {
|
|
|
14
14
|
cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType, getUtils,
|
|
15
15
|
} = require('../model/csnUtils');
|
|
16
16
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
17
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
18
17
|
const {
|
|
19
18
|
EdmTypeFacetMap,
|
|
20
19
|
EdmTypeFacetNames,
|
|
@@ -23,21 +22,34 @@ const {
|
|
|
23
22
|
const { getEdm } = require('./edm.js');
|
|
24
23
|
|
|
25
24
|
/*
|
|
26
|
-
OData V2 spec 06/01/2017 PDF version is available
|
|
25
|
+
OData V2 spec 06/01/2017 PDF version is available here:
|
|
27
26
|
https://msdn.microsoft.com/en-us/library/dd541474.aspx
|
|
28
27
|
*/
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @param {CSN.Model} _csn
|
|
31
|
+
* @param {string} serviceName
|
|
32
|
+
* @param {CSN.Options} _options
|
|
33
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
34
|
+
* @return {any}
|
|
35
|
+
*/
|
|
36
|
+
function csn2edm( _csn, serviceName, _options, messageFunctions ) {
|
|
37
|
+
return csn2edmAll(_csn, _options, [ serviceName ], messageFunctions)[serviceName];
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @param {CSN.Model} _csn
|
|
42
|
+
* @param {CSN.Options} _options
|
|
43
|
+
* @param {string[]|undefined} serviceNames
|
|
44
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
45
|
+
* @return {any}
|
|
46
|
+
*/
|
|
47
|
+
function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
35
48
|
// get us a fresh model copy that we can work with
|
|
36
49
|
const csn = cloneCsnNonDict(_csn, _options);
|
|
37
50
|
const special$self = !csn?.definitions?.$self && '$self';
|
|
51
|
+
messageFunctions.setModel(csn);
|
|
38
52
|
|
|
39
|
-
// use original options for messages; cloned CSN for semantic location
|
|
40
|
-
const messageFunctions = makeMessageFunction(csn, _options, 'to.edmx');
|
|
41
53
|
const {
|
|
42
54
|
info, warning, error, message, throwWithError,
|
|
43
55
|
} = messageFunctions;
|
|
@@ -477,7 +489,10 @@ function csn2edmAll( _csn, _options, serviceNames = undefined ) {
|
|
|
477
489
|
const type = `${schema.name}.${EntityTypeName}`;
|
|
478
490
|
if (properties.length === 0)
|
|
479
491
|
warning(null, location, { type }, 'EDM EntityType $(TYPE) has no properties');
|
|
480
|
-
|
|
492
|
+
// only if this entity has an entity set, it is required to have a key
|
|
493
|
+
// this especially covers: 'items: composition of one { data : String; }'
|
|
494
|
+
// "keyless" composition targets in structured containment mode
|
|
495
|
+
else if (entityCsn.$hasEntitySet && entityCsn.$edmKeyPaths.length === 0 && !isSingleton)
|
|
481
496
|
message('odata-spec-violation-no-key', location);
|
|
482
497
|
|
|
483
498
|
if (!edmUtils.isODataSimpleIdentifier(EntityTypeName))
|
|
@@ -627,15 +642,17 @@ function csn2edmAll( _csn, _options, serviceNames = undefined ) {
|
|
|
627
642
|
else if (isEdmPropertyRendered(elementCsn, options)) {
|
|
628
643
|
// CDXCORE-CDXCORE-173
|
|
629
644
|
// V2: filter @Core.MediaType
|
|
630
|
-
if (
|
|
645
|
+
if (options.isV2() && elementCsn['@Core.MediaType']) {
|
|
631
646
|
hasStream = elementCsn['@Core.MediaType'];
|
|
632
|
-
|
|
647
|
+
elementCsn['@cds.api.ignore'] = true;
|
|
633
648
|
// CDXCORE-CDXCORE-177:
|
|
634
649
|
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
635
|
-
// V4: render property type 'Edm.Stream'
|
|
636
650
|
streamProps.push(elementName);
|
|
637
651
|
}
|
|
638
652
|
else {
|
|
653
|
+
// V4: render property type 'Edm.Stream' but don't add '@Core.IsURL'
|
|
654
|
+
if ( elementCsn['@Core.MediaType'])
|
|
655
|
+
delete elementCsn['@Core.IsURL'];
|
|
639
656
|
collectUsedType(elementCsn);
|
|
640
657
|
props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
|
|
641
658
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
forEachDefinition, forEachMemberRecursively,
|
|
5
|
+
forEachDefinition, forEachMemberRecursively, isBuiltinType, getUtils,
|
|
6
6
|
} = require('../model/csnUtils');
|
|
7
|
+
const { assignAnnotation } = require('./edmUtils.js');
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line no-unused-vars
|
|
9
10
|
function resolveForeignKeyRefs( csn, csnUtils ) {
|
|
@@ -22,9 +23,11 @@ function resolveForeignKeyRefs( csn, csnUtils ) {
|
|
|
22
23
|
|
|
23
24
|
function inboundQualificationChecks( csn, options, messageFunctions,
|
|
24
25
|
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName, csnUtils ) {
|
|
25
|
-
const { message, throwWithError } = messageFunctions;
|
|
26
|
+
const { message, warning, throwWithError } = messageFunctions;
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
const { getFinalTypeInfo } = getUtils(csn);
|
|
29
|
+
|
|
30
|
+
forEachDefinition(csn, [ attach$path, onServiceMember ]);
|
|
28
31
|
checkNestedContextsAndServices();
|
|
29
32
|
throwWithError();
|
|
30
33
|
|
|
@@ -37,27 +40,67 @@ function inboundQualificationChecks( csn, options, messageFunctions,
|
|
|
37
40
|
}, [ 'definitions', defName ]);
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
// code that should be run only on service members
|
|
44
|
+
function onServiceMember( def, defName ) {
|
|
41
45
|
if (!isMyServiceRequested(defName))
|
|
42
46
|
return;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
|
|
48
|
+
const location = [ 'definitions', defName ];
|
|
49
|
+
// check items.items
|
|
50
|
+
checkIfItemsOfItems(def, undefined, undefined, location);
|
|
51
|
+
forEachMemberRecursively(def, checkIfItemsOfItems, location);
|
|
52
|
+
|
|
53
|
+
// decorate UUID keys with @Core.ComputedDefaultValue and complain
|
|
54
|
+
// on named type UUID elements that have no such annotation
|
|
55
|
+
const anno = '@Core.ComputedDefaultValue';
|
|
56
|
+
if (def.kind === 'entity' && def.elements) {
|
|
57
|
+
Object.entries(def.elements).forEach(([ eltName, elt ]) => {
|
|
58
|
+
if (elt.key)
|
|
59
|
+
addCoreComputedDefaultValueOnUUIDKeys(elt, [ eltName ], location);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function addCoreComputedDefaultValueOnUUIDKeys( elt, eltPath, path ) {
|
|
64
|
+
let type = elt.items?.type || elt.type;
|
|
65
|
+
if (type && !isBuiltinType(type)) {
|
|
66
|
+
type = getFinalTypeInfo(type);
|
|
67
|
+
if (!isBuiltinType(type.type))
|
|
68
|
+
path = [ 'definitions', type.type ];
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
type = elt;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (type.type === 'cds.UUID' && elt['@odata.foreignKey4'] == null) {
|
|
75
|
+
if (path[1] === defName)
|
|
76
|
+
assignAnnotation(elt, anno, true);
|
|
77
|
+
else if (elt[anno] == null)
|
|
78
|
+
warning('odata-key-uuid-default-anno', path, { type: type.type, anno, id: `${defName}:${eltPath.join('.')}` });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const elements = type.items?.elements || type.elements;
|
|
82
|
+
if (elements) {
|
|
83
|
+
Object.entries(elements).forEach(([ eltName, subelt ]) => {
|
|
84
|
+
addCoreComputedDefaultValueOnUUIDKeys(subelt, [ ...eltPath, eltName ], [ ...path, 'elements', eltName ]);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function checkIfItemsOfItems( member, _memberName, _prop, path ) {
|
|
91
|
+
const memberType = csnUtils.effectiveType(member);
|
|
92
|
+
if (memberType.items) {
|
|
93
|
+
if (memberType.items.target) {
|
|
94
|
+
const isComp = memberType.items.type === 'cds.Composition';
|
|
52
95
|
message('type-invalid-items', path, { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
53
96
|
return;
|
|
54
97
|
}
|
|
55
|
-
if (
|
|
98
|
+
if (memberType.items.items) {
|
|
56
99
|
message('chained-array-of', path);
|
|
57
100
|
return;
|
|
58
101
|
}
|
|
59
102
|
|
|
60
|
-
const itemsType = csnUtils.effectiveType(
|
|
103
|
+
const itemsType = csnUtils.effectiveType(memberType.items);
|
|
61
104
|
if (itemsType.items)
|
|
62
105
|
message('chained-array-of', path);
|
|
63
106
|
}
|
|
@@ -5,7 +5,7 @@ const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model')
|
|
|
5
5
|
const {
|
|
6
6
|
forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
7
7
|
isEdmPropertyRendered, getUtils, cloneCsnNonDict,
|
|
8
|
-
isBuiltinType, applyTransformations, cloneAnnotationValue, cardinality2str,
|
|
8
|
+
isBuiltinType, applyTransformations, cloneAnnotationValue, cardinality2str, isAnnotationExpression,
|
|
9
9
|
} = require('../model/csnUtils');
|
|
10
10
|
const edmUtils = require('./edmUtils.js');
|
|
11
11
|
const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
|
|
@@ -97,7 +97,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
97
97
|
*/
|
|
98
98
|
if (csn.meta && csn.meta.options && csn.meta.options.odataVersion === 'v4' && options.isV2()) {
|
|
99
99
|
// eslint-disable-next-line global-require
|
|
100
|
-
const { toFinalBaseType } = require('../transform/transformUtils').getTransformers(csn, options);
|
|
100
|
+
const { toFinalBaseType } = require('../transform/transformUtils').getTransformers(csn, options, messageFunctions);
|
|
101
101
|
expandCSNToFinalBaseType(csn, { toFinalBaseType }, csnUtils, serviceRootNames, options);
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -168,7 +168,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
168
168
|
initEdmNavPropBindingTargets,
|
|
169
169
|
pullupCapabilitiesAnnotations,
|
|
170
170
|
annotateOptionalActFuncParams,
|
|
171
|
-
openService,
|
|
172
171
|
]);
|
|
173
172
|
}
|
|
174
173
|
|
|
@@ -722,11 +721,11 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
722
721
|
// FK creation.
|
|
723
722
|
// The FK creation already propagates the annotations from the association
|
|
724
723
|
const elements = construct.items && construct.items.elements || construct.elements;
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
724
|
+
const assoc = elements[element['@odata.foreignKey4']];
|
|
725
|
+
if (assoc) {
|
|
726
|
+
Object.keys(assoc).filter(pn => pn[0] === '@' && !isAnnotationExpression(assoc[pn])).forEach((pn) => {
|
|
727
|
+
edmUtils.assignAnnotation(element, pn, assoc[pn]);
|
|
728
|
+
});
|
|
730
729
|
}
|
|
731
730
|
// and eventually remove some afterwards
|
|
732
731
|
if (options.isV2())
|
|
@@ -1075,8 +1074,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1075
1074
|
|
|
1076
1075
|
function muteNavProp( elt, msg = 'std' ) {
|
|
1077
1076
|
edmUtils.assignAnnotation(elt, '@odata.navigable', false);
|
|
1078
|
-
|
|
1079
|
-
|
|
1077
|
+
if (elt._target['@cds.autoexpose'] !== false) {
|
|
1078
|
+
warning('odata-navigation', [ 'definitions', struct.name, 'elements', elt.name ],
|
|
1079
|
+
{ target: elt._target.name, service: globalSchemaPrefix, '#': msg });
|
|
1080
|
+
}
|
|
1080
1081
|
}
|
|
1081
1082
|
|
|
1082
1083
|
function createSchemaRefFor( targetSchemaName ) {
|
|
@@ -2125,7 +2126,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2125
2126
|
optPns.forEach((op) => {
|
|
2126
2127
|
const type = op.items?.type || op.type;
|
|
2127
2128
|
if (type !== special$self)
|
|
2128
|
-
|
|
2129
|
+
error('odata-parameter-order', location.concat(op.name));
|
|
2129
2130
|
});
|
|
2130
2131
|
optPns = [];
|
|
2131
2132
|
}
|
|
@@ -2134,11 +2135,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2134
2135
|
}
|
|
2135
2136
|
}
|
|
2136
2137
|
|
|
2137
|
-
function openService( def ) {
|
|
2138
|
-
if (options.odataOpenType && !def.$isParamEntity && !def.$proxy)
|
|
2139
|
-
edmUtils.assignAnnotation(def, '@open', true);
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
2138
|
// ////////////////////////////////////////////////////////////////////
|
|
2143
2139
|
//
|
|
2144
2140
|
// Helper section starts here
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -551,8 +551,11 @@ function mapCdsToEdmType( csn, messageFunctions, isV2 = false, isMediaType = fal
|
|
|
551
551
|
Edm.GeometryCollection
|
|
552
552
|
*/
|
|
553
553
|
}[cdsType];
|
|
554
|
-
if (!edmType)
|
|
555
|
-
error(
|
|
554
|
+
if (!edmType) {
|
|
555
|
+
error('ref-unsupported-type', location, { type: cdsType, '#': 'odata' });
|
|
556
|
+
edmType = 'Edm.PrimitiveType';
|
|
557
|
+
}
|
|
558
|
+
|
|
556
559
|
|
|
557
560
|
if (isV2) {
|
|
558
561
|
if (edmType === 'Edm.Date')
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -801,6 +801,13 @@
|
|
|
801
801
|
"Parameter"
|
|
802
802
|
]
|
|
803
803
|
},
|
|
804
|
+
"Common.ValueListShowValuesImmediately": {
|
|
805
|
+
"Type": "Core.Tag",
|
|
806
|
+
"AppliesTo": [
|
|
807
|
+
"Annotation"
|
|
808
|
+
],
|
|
809
|
+
"$experimental": true
|
|
810
|
+
},
|
|
804
811
|
"Common.ValueListForValidation": {
|
|
805
812
|
"Type": "Edm.String",
|
|
806
813
|
"AppliesTo": [
|
|
@@ -2069,6 +2076,8 @@
|
|
|
2069
2076
|
"Type": "Validation.ConstraintType",
|
|
2070
2077
|
"AppliesTo": [
|
|
2071
2078
|
"Property",
|
|
2079
|
+
"NavigationProperty",
|
|
2080
|
+
"Parameter",
|
|
2072
2081
|
"EntityType",
|
|
2073
2082
|
"ComplexType"
|
|
2074
2083
|
]
|
|
@@ -2956,6 +2965,7 @@
|
|
|
2956
2965
|
"Common.IntervalType": {
|
|
2957
2966
|
"$kind": "ComplexType",
|
|
2958
2967
|
"Properties": {
|
|
2968
|
+
"Label": "Edm.String",
|
|
2959
2969
|
"LowerBoundary": "Edm.PropertyPath",
|
|
2960
2970
|
"LowerBoundaryIncluded": "Edm.Boolean",
|
|
2961
2971
|
"UpperBoundary": "Edm.PropertyPath",
|