@sap/cds-compiler 4.9.6 → 5.1.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 +92 -0
- package/bin/cds_remove_invalid_whitespace.js +2 -1
- package/bin/cdsc.js +49 -19
- package/bin/cdshi.js +3 -1
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +16 -19
- package/lib/api/options.js +5 -14
- package/lib/api/trace.js +0 -1
- package/lib/base/builtins.js +1 -0
- package/lib/base/location.js +4 -1
- package/lib/base/message-registry.js +43 -29
- package/lib/base/messages.js +23 -26
- package/lib/base/meta.js +10 -0
- package/lib/base/model.js +0 -2
- package/lib/base/node-helpers.js +0 -1
- package/lib/base/optionProcessorHelper.js +11 -0
- package/lib/checks/dbFeatureFlags.js +5 -0
- package/lib/checks/enricher.js +1 -5
- package/lib/checks/structuredAnnoExpressions.js +30 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -1
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/builtins.js +18 -2
- package/lib/compiler/checks.js +2 -5
- package/lib/compiler/define.js +8 -8
- package/lib/compiler/extend.js +108 -37
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/index.js +27 -10
- package/lib/compiler/lsp-api.js +501 -2
- package/lib/compiler/populate.js +60 -13
- package/lib/compiler/propagator.js +10 -8
- package/lib/compiler/resolve.js +117 -94
- package/lib/compiler/shared.js +114 -32
- package/lib/compiler/tweak-assocs.js +31 -21
- package/lib/compiler/utils.js +2 -1
- package/lib/compiler/xsn-model.js +4 -0
- package/lib/edm/annotations/genericTranslation.js +69 -35
- package/lib/edm/csn2edm.js +16 -4
- package/lib/edm/edm.js +10 -3
- package/lib/edm/edmAnnoPreprocessor.js +1 -2
- package/lib/edm/edmPreprocessor.js +8 -10
- package/lib/gen/Dictionary.json +66 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4995 -4817
- package/lib/json/csnVersion.js +1 -1
- package/lib/json/from-csn.js +4 -7
- package/lib/json/to-csn.js +25 -12
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/errorStrategy.js +0 -1
- package/lib/language/genericAntlrParser.js +35 -12
- package/lib/language/multiLineStringParser.js +3 -2
- package/lib/language/textUtils.js +1 -0
- package/lib/main.d.ts +28 -9
- package/lib/main.js +9 -9
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +22 -5
- package/lib/model/csnUtils.js +0 -14
- package/lib/model/revealInternalProperties.js +1 -2
- package/lib/modelCompare/compare.js +13 -11
- package/lib/optionProcessor.js +30 -9
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +44 -14
- package/lib/render/toHdbcds.js +1 -2
- package/lib/render/toSql.js +45 -8
- package/lib/render/utils/common.js +12 -9
- package/lib/render/utils/stringEscapes.js +1 -0
- package/lib/transform/db/applyTransformations.js +13 -8
- package/lib/transform/db/associations.js +62 -54
- package/lib/transform/db/backlinks.js +20 -5
- package/lib/transform/db/expansion.js +1 -6
- package/lib/transform/db/flattening.js +86 -109
- package/lib/transform/db/killAnnotations.js +3 -0
- package/lib/transform/db/processSqlServices.js +63 -0
- package/lib/transform/db/temporal.js +3 -4
- package/lib/transform/db/views.js +0 -1
- package/lib/transform/draft/odata.js +56 -3
- package/lib/transform/effective/annotations.js +3 -2
- package/lib/transform/effective/flattening.js +135 -0
- package/lib/transform/effective/main.js +6 -4
- package/lib/transform/effective/types.js +13 -9
- package/lib/transform/forOdata.js +0 -2
- package/lib/transform/forRelationalDB.js +9 -19
- package/lib/transform/localized.js +7 -8
- package/lib/transform/odata/flattening.js +39 -31
- package/lib/transform/odata/typesExposure.js +5 -17
- package/lib/transform/transformUtils.js +1 -1
- package/lib/transform/translateAssocsToJoins.js +0 -1
- package/lib/utils/file.js +87 -8
- package/lib/utils/moduleResolve.js +59 -8
- package/lib/utils/term.js +3 -2
- package/package.json +7 -3
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/type-unexpected-foreign-keys.md +52 -0
- package/share/messages/type-unexpected-on-condition.md +52 -0
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
const {
|
|
6
6
|
forEachGeneric,
|
|
7
7
|
forEachInOrder,
|
|
8
|
-
isBetaEnabled,
|
|
9
8
|
} = require('../base/model');
|
|
10
9
|
const { dictLocation, weakLocation, weakRefLocation } = require('../base/location');
|
|
11
10
|
|
|
@@ -39,16 +38,17 @@ function tweakAssocs( model ) {
|
|
|
39
38
|
checkOnCondition,
|
|
40
39
|
effectiveType,
|
|
41
40
|
getOrigin,
|
|
41
|
+
extendForeignKeys,
|
|
42
|
+
createRemainingAnnotateStatements,
|
|
43
|
+
mergeSpecifiedForeignKeys,
|
|
42
44
|
} = model.$functions;
|
|
43
45
|
|
|
44
46
|
Object.assign(model.$functions, {
|
|
45
47
|
firstProjectionForPath,
|
|
46
48
|
});
|
|
47
49
|
|
|
48
|
-
const isV5preview = isBetaEnabled( model.options, 'v5preview' );
|
|
49
|
-
|
|
50
50
|
// Phase 5: rewrite associations
|
|
51
|
-
model._entities.forEach( rewriteArtifact );
|
|
51
|
+
model._entities.forEach( rewriteArtifact ); // _entities contains all definitions, sorted.
|
|
52
52
|
// Think hard whether an on condition rewrite can lead to a new cyclic
|
|
53
53
|
// dependency. If so, we need other messages anyway. TODO: probably dox
|
|
54
54
|
// another cyclic check with testMode.js
|
|
@@ -59,6 +59,11 @@ function tweakAssocs( model ) {
|
|
|
59
59
|
if (art.kind === 'select')
|
|
60
60
|
forEachQueryExpr( art, checkExpr );
|
|
61
61
|
} );
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// create “super” ANNOTATE statements for annotations on unknown artifacts:
|
|
65
|
+
createRemainingAnnotateStatements();
|
|
66
|
+
|
|
62
67
|
return;
|
|
63
68
|
|
|
64
69
|
|
|
@@ -71,7 +76,6 @@ function tweakAssocs( model ) {
|
|
|
71
76
|
// return;
|
|
72
77
|
if (!art.query) {
|
|
73
78
|
rewriteAssociation( art );
|
|
74
|
-
forEachGeneric( art, 'elements', rewriteAssociation );
|
|
75
79
|
}
|
|
76
80
|
else {
|
|
77
81
|
traverseQueryExtra( art, ( query ) => {
|
|
@@ -81,9 +85,11 @@ function tweakAssocs( model ) {
|
|
|
81
85
|
if (art._service)
|
|
82
86
|
forEachGeneric( art, 'elements', complainAboutTargetOutsideService );
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
if (art.query) {
|
|
89
|
+
traverseQueryPost(art.query, false, (query) => {
|
|
90
|
+
forEachGeneric( query, 'elements', rewriteAssociationCheck );
|
|
91
|
+
});
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
// function rewriteView( view ) {
|
|
@@ -111,9 +117,7 @@ function tweakAssocs( model ) {
|
|
|
111
117
|
info( 'assoc-target-not-in-service', loc,
|
|
112
118
|
{ target, '#': (elem._main.query ? 'select' : 'define') }, {
|
|
113
119
|
std: 'Target $(TARGET) of association is outside any service', // not used
|
|
114
|
-
// eslint-disable-next-line max-len
|
|
115
120
|
define: 'Target $(TARGET) of explicitly defined association is outside any service',
|
|
116
|
-
// eslint-disable-next-line max-len
|
|
117
121
|
select: 'Target $(TARGET) of explicitly selected association is outside any service',
|
|
118
122
|
} );
|
|
119
123
|
}
|
|
@@ -129,7 +133,7 @@ function tweakAssocs( model ) {
|
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
function rewriteAssociationCheck( element ) {
|
|
132
|
-
const elem = element.items || element; // TODO
|
|
136
|
+
const elem = element.items || element; // TODO v6: nested items
|
|
133
137
|
if (elem.elements)
|
|
134
138
|
forEachGeneric( elem, 'elements', rewriteAssociationCheck );
|
|
135
139
|
if (!elem.target)
|
|
@@ -236,13 +240,24 @@ function tweakAssocs( model ) {
|
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
function rewriteAssociation( element ) {
|
|
239
|
-
|
|
243
|
+
doRewriteAssociation( element );
|
|
244
|
+
if (element.target) {
|
|
245
|
+
extendForeignKeys( element );
|
|
246
|
+
if (element.foreignKeys$)
|
|
247
|
+
mergeSpecifiedForeignKeys( element );
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// only to be used by rewriteAssociation()
|
|
252
|
+
function doRewriteAssociation( element ) {
|
|
253
|
+
let elem = element.items || element; // TODO v6: nested items
|
|
240
254
|
if (elem.elements)
|
|
241
255
|
forEachGeneric( elem, 'elements', rewriteAssociation );
|
|
242
256
|
if (elem.targetAspect?.elements)
|
|
243
257
|
forEachGeneric( elem.targetAspect, 'elements', rewriteAssociation );
|
|
244
258
|
if (!originTarget( elem ))
|
|
245
259
|
return;
|
|
260
|
+
|
|
246
261
|
// console.log(message( null, elem.location, elem,
|
|
247
262
|
// {art:assoc,target,ftype:JSON.stringify(ftype)}, 'Info','RA').toString())
|
|
248
263
|
|
|
@@ -300,6 +315,7 @@ function tweakAssocs( model ) {
|
|
|
300
315
|
}
|
|
301
316
|
}
|
|
302
317
|
|
|
318
|
+
/** Returns the element's origin's target artifact. */
|
|
303
319
|
function originTarget( elem ) {
|
|
304
320
|
const assoc = !elem.expand && getOrigin( elem );
|
|
305
321
|
const ftype = assoc && effectiveType( assoc );
|
|
@@ -378,6 +394,9 @@ function tweakAssocs( model ) {
|
|
|
378
394
|
traverseExpr( elem.on, 'rewrite-on', elem,
|
|
379
395
|
expr => rewriteExpr( expr, elem, nav.tableAlias ) );
|
|
380
396
|
}
|
|
397
|
+
else if (elem._pathHead) {
|
|
398
|
+
error( 'rewrite-not-supported', [ elem.target.location, elem ] );
|
|
399
|
+
}
|
|
381
400
|
else {
|
|
382
401
|
// TODO: support that, now that the ON condition is rewritten in the right order
|
|
383
402
|
error( null, [ elem.value.location, elem ], {},
|
|
@@ -456,15 +475,6 @@ function tweakAssocs( model ) {
|
|
|
456
475
|
};
|
|
457
476
|
setArtifactLink( elem.type, assocType._artifact );
|
|
458
477
|
|
|
459
|
-
if (!isV5preview) { // TODO(v5): Remove, only use $enclosed
|
|
460
|
-
elem.$filtered = {
|
|
461
|
-
val: true,
|
|
462
|
-
literal: 'boolean',
|
|
463
|
-
location,
|
|
464
|
-
$inferred: '$generated',
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
|
|
468
478
|
const isComp = (getUnderlyingBuiltinType( assoc )?.name?.id === 'cds.Composition');
|
|
469
479
|
if (isComp) {
|
|
470
480
|
elem.$enclosed = {
|
package/lib/compiler/utils.js
CHANGED
|
@@ -125,7 +125,8 @@ function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDepreca
|
|
|
125
125
|
// TODO throughout the compiler: do not set art.‹prop›.$inferred if art.$inferred
|
|
126
126
|
if (kind)
|
|
127
127
|
member.kind = kind;
|
|
128
|
-
else if (origin.key && !tmpDeprecated)
|
|
128
|
+
else if (origin.key && !tmpDeprecated)
|
|
129
|
+
// TODO(v6): remove tmpDeprecated once `noKeyPropagationWithExpansions` is removed
|
|
129
130
|
member.key = Object.assign( { $inferred: 'expanded' }, origin.key );
|
|
130
131
|
if (kind && origin.masked) // TODO: remove!
|
|
131
132
|
member.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { isEdmPropertyRendered, transformExpression } = require('../../model/csnUtils');
|
|
4
|
-
const { isBuiltinType } = require('../../base/builtins');
|
|
4
|
+
const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
|
|
5
5
|
const edmUtils = require('../edmUtils.js');
|
|
6
6
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
7
7
|
const preprocessAnnotations = require('./preprocessAnnotations.js');
|
|
@@ -60,6 +60,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
60
60
|
if (defName.startsWith(`${serviceName}.`))
|
|
61
61
|
assignParameterAnnotations(def);
|
|
62
62
|
});
|
|
63
|
+
|
|
63
64
|
// Crawl over the csn and trigger the annotation translation for all kinds
|
|
64
65
|
// of annotated things.
|
|
65
66
|
// Note: only works for single service
|
|
@@ -103,6 +104,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
103
104
|
const scopeCheck = {
|
|
104
105
|
ref: (elemref, prop, xpr, path) => {
|
|
105
106
|
if (scopeCheck.scope === 'param' &&
|
|
107
|
+
!isMagicVariable(xpr[0]) &&
|
|
106
108
|
(!elemref.param ||
|
|
107
109
|
(xpr[0].id || xpr[0]) === '$self' && !def.elements.$self)) {
|
|
108
110
|
error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaparam' });
|
|
@@ -221,13 +223,16 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
221
223
|
|
|
222
224
|
function handleDefinition( defName, def, location ) {
|
|
223
225
|
// definition bound annotations
|
|
224
|
-
handleAnnotations(defName, def, location);
|
|
226
|
+
handleAnnotations(defName, def, { location });
|
|
225
227
|
// definition bound element annotations
|
|
226
228
|
if (def.$elementsAnnoProxies) {
|
|
227
229
|
Object.entries(def.$elementsAnnoProxies).forEach(([ elemPath, element ]) => {
|
|
228
230
|
const edmTargetName = `${defName}/${elemPath}`;
|
|
229
|
-
handleAnnotations(edmTargetName, element,
|
|
230
|
-
|
|
231
|
+
handleAnnotations(edmTargetName, element,
|
|
232
|
+
{
|
|
233
|
+
location: element.$path,
|
|
234
|
+
csnPath: [ ...location, '$elementsAnnoProxies', elemPath ],
|
|
235
|
+
});
|
|
231
236
|
});
|
|
232
237
|
}
|
|
233
238
|
// element bound annotations
|
|
@@ -235,7 +240,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
235
240
|
Object.entries(def.elements).forEach(([ elemName, element ]) => {
|
|
236
241
|
const edmTargetName = `${defName}/${elemName}`;
|
|
237
242
|
const eLocation = [ ...location, 'elements', elemName ];
|
|
238
|
-
handleAnnotations(edmTargetName, element, eLocation);
|
|
243
|
+
handleAnnotations(edmTargetName, element, { location: eLocation });
|
|
239
244
|
});
|
|
240
245
|
}
|
|
241
246
|
// bound actions
|
|
@@ -274,6 +279,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
274
279
|
// entityNameIfBound : qualified name of entity if bound action/function
|
|
275
280
|
function handleAction( cActionName, cAction, entityNameIfBound, location ) {
|
|
276
281
|
let actionName = cActionName;
|
|
282
|
+
|
|
277
283
|
if (isV2()) { // Replace up to last dot with <serviceName>.EntityContainer
|
|
278
284
|
const lastDotIndex = actionName.lastIndexOf('.');
|
|
279
285
|
if (lastDotIndex > -1)
|
|
@@ -283,55 +289,70 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
283
289
|
actionName += relParList();
|
|
284
290
|
}
|
|
285
291
|
|
|
286
|
-
handleAnnotations(actionName, cAction, location);
|
|
292
|
+
handleAnnotations(actionName, cAction, { location, cAction });
|
|
287
293
|
|
|
288
294
|
if (cAction.params) {
|
|
289
295
|
if (cAction.$paramsAnnoProxies) {
|
|
290
296
|
Object.entries(cAction.$paramsAnnoProxies).forEach(([ paramPath, param ]) => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
297
|
+
// skip explicit binding parameter in V2
|
|
298
|
+
if (!(options.isV2() && param.type === '$self' && paramPath === cAction.$bindingParam?.name)) {
|
|
299
|
+
const edmTargetName = `${actionName}/${paramPath}`;
|
|
300
|
+
handleAnnotations(edmTargetName, param,
|
|
301
|
+
{
|
|
302
|
+
location: param.$path,
|
|
303
|
+
csnPath: [ ...location, '$paramsAnnoProxies', paramPath ],
|
|
304
|
+
cAction,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
294
307
|
});
|
|
295
308
|
}
|
|
309
|
+
// explicit binding parameter is removed from params in V2 during
|
|
310
|
+
// createActionV2(), no need to check ;)
|
|
296
311
|
Object.entries(cAction.params).forEach(([ n, p ]) => {
|
|
297
312
|
const edmTargetName = `${actionName}/${n}`;
|
|
298
313
|
handleAnnotations(edmTargetName, p,
|
|
299
|
-
|
|
314
|
+
{
|
|
315
|
+
action: true,
|
|
316
|
+
location: [ ...location, 'params', n ],
|
|
317
|
+
cAction,
|
|
318
|
+
});
|
|
300
319
|
});
|
|
301
320
|
}
|
|
302
321
|
if (cAction.returns) {
|
|
303
322
|
if (cAction.$returnsAnnoProxies) {
|
|
304
323
|
Object.entries(cAction.$returnsAnnoProxies).forEach(([ returnsPath, returns ]) => {
|
|
305
324
|
const edmTargetName = `${actionName}/${returnsPath}`;
|
|
306
|
-
handleAnnotations(edmTargetName, returns,
|
|
307
|
-
|
|
325
|
+
handleAnnotations(edmTargetName, returns,
|
|
326
|
+
{
|
|
327
|
+
location: returns.$path,
|
|
328
|
+
csnPath: [ ...location, '$returnsAnnoProxies', returnsPath ],
|
|
329
|
+
cAction,
|
|
330
|
+
});
|
|
308
331
|
});
|
|
309
332
|
}
|
|
310
333
|
const edmTargetName = `${actionName}/$ReturnType`;
|
|
311
334
|
setProp(cAction.returns, '$appliesToReturnType', true);
|
|
312
335
|
handleAnnotations(edmTargetName, cAction.returns,
|
|
313
|
-
|
|
336
|
+
{
|
|
337
|
+
location: [ ...location, 'returns' ],
|
|
338
|
+
cAction,
|
|
339
|
+
});
|
|
314
340
|
delete cAction.returns.$appliesToReturnType;
|
|
315
341
|
}
|
|
316
342
|
|
|
317
343
|
function relParList() {
|
|
318
|
-
|
|
344
|
+
// we rely on the order of params in the csn being the correct one
|
|
319
345
|
const params = [];
|
|
320
346
|
if (entityNameIfBound) {
|
|
321
347
|
// If this is an action and has an explicit binding parameter add it here
|
|
322
|
-
if (cAction.$bindingParam && cAction.kind === 'action'
|
|
348
|
+
if (cAction.$bindingParam && cAction.kind === 'action' ||
|
|
349
|
+
cAction.$bindingParam?.viaAnno) {
|
|
323
350
|
params.push(
|
|
324
351
|
cAction.$bindingParam.items
|
|
325
352
|
? `Collection(${entityNameIfBound})`
|
|
326
353
|
: entityNameIfBound
|
|
327
354
|
);
|
|
328
355
|
}
|
|
329
|
-
// If action/function has no explicit binding parameter add it here
|
|
330
|
-
else if (!cAction.$bindingParam) {
|
|
331
|
-
params.push(cAction['@cds.odata.bindingparameter.collection']
|
|
332
|
-
? `Collection(${entityNameIfBound})`
|
|
333
|
-
: entityNameIfBound);
|
|
334
|
-
}
|
|
335
356
|
}
|
|
336
357
|
// In case this is a function the explicit binding parameter is part of
|
|
337
358
|
// the functions params dictionary. Only for functions all parameters must
|
|
@@ -349,12 +370,12 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
349
370
|
return edmUtils.mapCdsToEdmType(p, messageFunctions, false /* is only called for v4 */);
|
|
350
371
|
}
|
|
351
372
|
else if (options.whatsMySchemaName) {
|
|
352
|
-
const schemaName = options.whatsMySchemaName(p.type);
|
|
373
|
+
const schemaName = options.whatsMySchemaName(p._edmType || p.type);
|
|
353
374
|
// strip the service namespace of from a parameter type
|
|
354
375
|
if (schemaName && schemaName !== options.serviceName)
|
|
355
|
-
return p.type.replace(`${options.serviceName}.`, '');
|
|
376
|
+
return (p._edmType || p.type).replace(`${options.serviceName}.`, '');
|
|
356
377
|
}
|
|
357
|
-
return p.type;
|
|
378
|
+
return p._edmType || p.type;
|
|
358
379
|
}
|
|
359
380
|
}
|
|
360
381
|
}
|
|
@@ -364,7 +385,9 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
364
385
|
// edmTargetName : string, name of the target in edm
|
|
365
386
|
// carrier: object, the annotated cds thing, contains all the annotations
|
|
366
387
|
// as properties with names starting with @
|
|
367
|
-
|
|
388
|
+
// ctx: locations and other information that is required to write the
|
|
389
|
+
// annotations
|
|
390
|
+
function handleAnnotations( edmTargetName, carrier, ctx ) {
|
|
368
391
|
// collect the names of the carrier's annotation properties
|
|
369
392
|
// keep only those annotations that - start with a known vocabulary name
|
|
370
393
|
// - have a value other than null
|
|
@@ -378,6 +401,8 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
378
401
|
(isV2() && (edmUtils.isDerivedType(carrier))))
|
|
379
402
|
return;
|
|
380
403
|
|
|
404
|
+
if (ctx.location == null)
|
|
405
|
+
throw Error('location required');
|
|
381
406
|
|
|
382
407
|
// Filter unknown toplevel annotations
|
|
383
408
|
// Final filtering of all annotations is done in handleTerm
|
|
@@ -397,15 +422,15 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
397
422
|
transformExpression(carrier, knownAnno, {
|
|
398
423
|
ref: (elemref, prop, xpr, csnPath) => {
|
|
399
424
|
if (options.isV2() && elemref.$bparam) {
|
|
400
|
-
error('odata-anno-xpr-ref', location, {
|
|
401
|
-
elemref, anno: knownAnno, version: '2.0', '#': '
|
|
425
|
+
error('odata-anno-xpr-ref', ctx.location, {
|
|
426
|
+
elemref, anno: knownAnno, version: '2.0', '#': 'bparam_v2_expl',
|
|
402
427
|
});
|
|
403
428
|
return;
|
|
404
429
|
}
|
|
405
430
|
const { links, scope } = reqDefsUtils.inspectRef(csnPath);
|
|
406
431
|
let i = scope === '$self' ? 1 : 0;
|
|
407
432
|
if (scope === '$magic') {
|
|
408
|
-
error('odata-anno-xpr-ref', location, {
|
|
433
|
+
error('odata-anno-xpr-ref', ctx.location, {
|
|
409
434
|
elemref, anno: knownAnno, '#': 'magic',
|
|
410
435
|
});
|
|
411
436
|
return;
|
|
@@ -413,7 +438,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
413
438
|
let stop = false;
|
|
414
439
|
for (; i < links.length && !stop; i++) {
|
|
415
440
|
if (!isEdmPropertyRendered(links[i].art, csnPath)) {
|
|
416
|
-
error('odata-anno-xpr-ref', location, {
|
|
441
|
+
error('odata-anno-xpr-ref', ctx.location, {
|
|
417
442
|
count: i + 1, elemref, anno: knownAnno, '#': 'notrendered',
|
|
418
443
|
});
|
|
419
444
|
stop = true;
|
|
@@ -422,16 +447,27 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
422
447
|
const proxy = links[i].art?._target;
|
|
423
448
|
const eltName = links[i + 1].art?.name;
|
|
424
449
|
if (!proxy.elements[eltName]) {
|
|
425
|
-
error('odata-anno-xpr-ref', location, {
|
|
450
|
+
error('odata-anno-xpr-ref', ctx.location, {
|
|
426
451
|
count: i + 2, elemref, anno: knownAnno, '#': 'notrendered',
|
|
427
452
|
});
|
|
428
453
|
stop = true;
|
|
429
454
|
}
|
|
430
455
|
}
|
|
431
456
|
}
|
|
457
|
+
if (!stop && ctx.cAction?.$isBound && scope === '$self') {
|
|
458
|
+
if (options.isV2()) {
|
|
459
|
+
error('odata-anno-xpr-ref', ctx.location, {
|
|
460
|
+
elemref, anno: knownAnno, version: '2.0', '#': 'bparam_v2_impl',
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
xpr[0] = ctx.cAction.$bindingParam.name;
|
|
465
|
+
elemref.param = true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
432
468
|
},
|
|
433
|
-
},
|
|
434
|
-
xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
|
|
469
|
+
}, ctx.csnPath || ctx.location);
|
|
470
|
+
xpr2edmJson(carrier, knownAnno, ctx.location, options, messageFunctions);
|
|
435
471
|
}
|
|
436
472
|
});
|
|
437
473
|
|
|
@@ -458,7 +494,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
458
494
|
const alternativeAnnotations = [];
|
|
459
495
|
|
|
460
496
|
// now create annotation objects for all the annotations of carrier
|
|
461
|
-
handleAnno2(addAnnotation, prefixTree, location);
|
|
497
|
+
handleAnno2(addAnnotation, prefixTree, ctx.location);
|
|
462
498
|
|
|
463
499
|
// Produce Edm.Annotations and attach collected Edm.Annotation(s) to the
|
|
464
500
|
// envelope (or directly to the Schema)
|
|
@@ -507,7 +543,6 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
507
543
|
}
|
|
508
544
|
|
|
509
545
|
function initCarrierControlVars() {
|
|
510
|
-
// eslint-disable-next-line no-unused-vars
|
|
511
546
|
let testToStandardEdmTargetP = () => true; // if true, assign to standard Edm Target
|
|
512
547
|
let stdEdmTargetNameP = edmTargetName;
|
|
513
548
|
let alternativeEdmTargetNameP = null;
|
|
@@ -783,7 +818,6 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
783
818
|
// addAnnotationFunc needs AppliesTo message from dictionary to decide where to put the anno
|
|
784
819
|
const termName = fullTermName.replace(/#(\w+)$/g, ''); // remove qualifier
|
|
785
820
|
const dictTerm = getDictTerm(termName, msg); // message for unknown term was already issued in handleTerm
|
|
786
|
-
// eslint-disable-next-line sonarjs/no-collapsible-if
|
|
787
821
|
if (!addAnnotationFunc(anno, dictTerm && dictTerm.AppliesTo)) {
|
|
788
822
|
if (dictTerm && dictTerm.AppliesTo) {
|
|
789
823
|
message('odata-anno-def', location,
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -732,11 +732,13 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
732
732
|
if (type === special$self) {
|
|
733
733
|
bpName = entries[0][0];
|
|
734
734
|
setProp(actionCsn, '$bindingParam', firstParam);
|
|
735
|
+
// preserve the original type (as it is the key to reqDefs.defintions)
|
|
736
|
+
// for annotation path resolution (eg. for $draft.IsActiveEntity)
|
|
735
737
|
if (bpType) {
|
|
736
738
|
if (firstParam.items?.type)
|
|
737
|
-
firstParam.items
|
|
739
|
+
setProp(firstParam.items, '_edmType', bpType);
|
|
738
740
|
if (firstParam.type)
|
|
739
|
-
firstParam
|
|
741
|
+
setProp(firstParam, '_edmType', bpType);
|
|
740
742
|
}
|
|
741
743
|
if (!edmUtils.isODataSimpleIdentifier(bpName))
|
|
742
744
|
message('odata-spec-violation-id', [ ...location, 'params', bpName ], { id: bpName });
|
|
@@ -775,11 +777,20 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
775
777
|
if (entityCsn) {
|
|
776
778
|
actionNode.setEdmAttribute('IsBound', true);
|
|
777
779
|
if (!actionCsn.$bindingParam) {
|
|
780
|
+
const bpDef = {
|
|
781
|
+
name: bpName,
|
|
782
|
+
viaAnno: true,
|
|
783
|
+
};
|
|
778
784
|
// Binding Parameter: 'in' at first position in sequence, this is decisive!
|
|
779
|
-
if (actionCsn['@cds.odata.bindingparameter.collection'])
|
|
785
|
+
if (actionCsn['@cds.odata.bindingparameter.collection']) {
|
|
780
786
|
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection: true/* , Nullable: false */ } ));
|
|
781
|
-
|
|
787
|
+
bpDef.items = { type: bpType };
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
782
790
|
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType } ));
|
|
791
|
+
bpDef.type = bpType;
|
|
792
|
+
}
|
|
793
|
+
setProp(actionCsn, '$bindingParam', bpDef);
|
|
783
794
|
}
|
|
784
795
|
}
|
|
785
796
|
else if (EntityContainer) { // unbound => produce Action/FunctionImport
|
|
@@ -891,6 +902,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
891
902
|
if (i === 0 && type === special$self) {
|
|
892
903
|
// skip and remove the first parameter if it is a $self binding parameter to
|
|
893
904
|
// omit annotation rendering later on
|
|
905
|
+
setProp(actionCsn, '$bindingParam', parameterCsn);
|
|
894
906
|
delete actionCsn.params[parameterName];
|
|
895
907
|
}
|
|
896
908
|
else {
|
package/lib/edm/edm.js
CHANGED
|
@@ -625,7 +625,8 @@ function getEdm( options, messageFunctions ) {
|
|
|
625
625
|
edmUtils.addTypeFacets(this, this._scalarType);
|
|
626
626
|
}
|
|
627
627
|
else {
|
|
628
|
-
|
|
628
|
+
// it's either _edmType or type (_edmType only used for explicit binding param)
|
|
629
|
+
this._edmAttributes[typeName] = typecsn._edmType || typecsn.type;
|
|
629
630
|
}
|
|
630
631
|
}
|
|
631
632
|
// CDXCORE-245:
|
|
@@ -882,12 +883,20 @@ function getEdm( options, messageFunctions ) {
|
|
|
882
883
|
class ReturnType extends PropertyBase {
|
|
883
884
|
constructor(version, csn) {
|
|
884
885
|
super(version, {}, csn);
|
|
886
|
+
// CSDL 12.8: If the return type is a collection of entity types,
|
|
887
|
+
// the Nullable attribute has no meaning and MUST NOT be specified.
|
|
888
|
+
if (csn.$NoNullableProperty)
|
|
889
|
+
delete this._edmAttributes.Nullable;
|
|
885
890
|
}
|
|
886
891
|
|
|
887
892
|
// we need Name but NO $kind, can't use standard to JSON()
|
|
888
893
|
toJSON() {
|
|
889
894
|
const json = Object.create(null);
|
|
890
895
|
this.toJSONattributes(json);
|
|
896
|
+
// CSDL 12.8: If the return type is a collection of entity types,
|
|
897
|
+
// the Nullable attribute has no meaning and MUST NOT be specified.
|
|
898
|
+
if (this._csn.$NoNullableProperty)
|
|
899
|
+
delete json.$Nullable;
|
|
891
900
|
return json;
|
|
892
901
|
}
|
|
893
902
|
}
|
|
@@ -897,7 +906,6 @@ function getEdm( options, messageFunctions ) {
|
|
|
897
906
|
super(version, attributes, csn);
|
|
898
907
|
// TIPHANACDS-4180
|
|
899
908
|
if (this.v2) {
|
|
900
|
-
// eslint-disable-next-line sonarjs/no-redundant-boolean
|
|
901
909
|
if (csn['@odata.etag'] || csn['@cds.etag'])
|
|
902
910
|
this._edmAttributes.ConcurrencyMode = 'Fixed';
|
|
903
911
|
|
|
@@ -1037,7 +1045,6 @@ function getEdm( options, messageFunctions ) {
|
|
|
1037
1045
|
See csn2edm.createEntityTypeAndSet() for details
|
|
1038
1046
|
2) ContainsTarget stems from the @odata.contained annotation
|
|
1039
1047
|
*/
|
|
1040
|
-
// eslint-disable-next-line sonarjs/no-redundant-boolean
|
|
1041
1048
|
if (csn['@odata.contained'] || csn.containsTarget)
|
|
1042
1049
|
this._edmAttributes.ContainsTarget = true;
|
|
1043
1050
|
|
|
@@ -59,7 +59,6 @@ function applyAppSpecificLateCsnTransformationOnElement( options, element, struc
|
|
|
59
59
|
// runtime cannot set that okra flag (alternatively the runtime has to search
|
|
60
60
|
// for @[odata|cds].etag annotations...
|
|
61
61
|
if (options.isV4() && (element['@odata.etag'] || element['@cds.etag'])) {
|
|
62
|
-
// eslint-disable-next-line sonarjs/no-redundant-boolean
|
|
63
62
|
// don't put element name into collection as per advice from Ralf Handl, as
|
|
64
63
|
// no runtime is interested in the property itself, it is sufficient to mark
|
|
65
64
|
// the entity set.
|
|
@@ -243,7 +242,7 @@ function setSAPSpecificV2AnnotationsToEntitySet( options, carrier ) {
|
|
|
243
242
|
'@sap.updatable.path': addToSetAttr,
|
|
244
243
|
'@sap.deletable.path': addToSetAttr,
|
|
245
244
|
'@sap.searchable': addToSetAttr,
|
|
246
|
-
'@sap.
|
|
245
|
+
'@sap.pageable': addToSetAttr,
|
|
247
246
|
'@sap.topable': addToSetAttr,
|
|
248
247
|
'@sap.countable': addToSetAttr,
|
|
249
248
|
'@sap.addressable': addToSetAttr,
|
|
@@ -100,7 +100,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
100
100
|
as a step in the OData transformer with the goal to have a protocol agnostic OData CSN.
|
|
101
101
|
*/
|
|
102
102
|
if (csn.meta && csn.meta.options && csn.meta.options.odataVersion === 'v4' && options.isV2()) {
|
|
103
|
-
// eslint-disable-next-line global-require
|
|
104
103
|
const { toFinalBaseType } = require('../transform/transformUtils').getTransformers(csn, options, messageFunctions);
|
|
105
104
|
expandCSNToFinalBaseType(csn, { toFinalBaseType }, csnUtils, serviceRootNames, options);
|
|
106
105
|
}
|
|
@@ -704,11 +703,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
704
703
|
validFrom.push(element);
|
|
705
704
|
|
|
706
705
|
// forward annotations from managed association element to its foreign keys
|
|
707
|
-
// TODO: Try to eliminate this code by rearranging
|
|
708
|
-
// forOdata::addCommonValueListviaAssociation(member, memberName)
|
|
709
|
-
// such that the VL annotations are distributed to the associations *before*
|
|
710
|
-
// FK creation.
|
|
711
|
-
// The FK creation already propagates the annotations from the association
|
|
712
706
|
const elements = construct.items?.elements || construct.elements;
|
|
713
707
|
const assoc = elements[element['@odata.foreignKey4']];
|
|
714
708
|
if (assoc) {
|
|
@@ -1892,7 +1886,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1892
1886
|
mapCdsToEdmProp(def);
|
|
1893
1887
|
annotateAllowedValues(def, defLocation);
|
|
1894
1888
|
if (def.returns) {
|
|
1895
|
-
markCollection(def.returns);
|
|
1889
|
+
markCollection(def.returns, true);
|
|
1896
1890
|
mapCdsToEdmProp(def.returns);
|
|
1897
1891
|
annotateAllowedValues(def.returns, [ ...defLocation, 'returns' ]);
|
|
1898
1892
|
}
|
|
@@ -1905,17 +1899,21 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1905
1899
|
rewriteAnnotationExpressions(member);
|
|
1906
1900
|
if (member.returns) {
|
|
1907
1901
|
edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
|
|
1908
|
-
markCollection(member.returns);
|
|
1902
|
+
markCollection(member.returns, true);
|
|
1909
1903
|
mapCdsToEdmProp(member.returns);
|
|
1910
1904
|
annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
|
|
1911
1905
|
rewriteAnnotationExpressions(member.returns);
|
|
1912
1906
|
}
|
|
1913
1907
|
}, defLocation);
|
|
1914
1908
|
// mark members that need to be rendered as collections
|
|
1915
|
-
function markCollection( obj ) {
|
|
1909
|
+
function markCollection( obj, isReturns ) {
|
|
1916
1910
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
1917
1911
|
if (items) {
|
|
1918
|
-
edmUtils.assignProp(obj, '
|
|
1912
|
+
edmUtils.assignProp(obj, '$NoNullableProperty',
|
|
1913
|
+
isReturns && items.type &&
|
|
1914
|
+
!isBuiltinType(items.type) &&
|
|
1915
|
+
csn.definitions[items.type]?.kind === 'entity');
|
|
1916
|
+
edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : false);
|
|
1919
1917
|
edmUtils.assignProp(obj, '$isCollection', true);
|
|
1920
1918
|
}
|
|
1921
1919
|
}
|