@sap/cds-compiler 6.7.3 → 6.9.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 +70 -0
- package/README.md +4 -0
- package/bin/cdsc.js +5 -5
- package/bin/cdshi.js +1 -0
- package/bin/cdsse.js +1 -1
- package/lib/api/main.js +17 -9
- package/lib/api/options.js +5 -2
- package/lib/api/validate.js +1 -1
- package/lib/base/builtins.js +13 -9
- package/lib/{model → base}/csnRefs.js +8 -10
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +68 -4
- package/lib/base/messages.js +4 -2
- package/lib/{optionProcessor.js → base/optionProcessor.js} +5 -3
- package/lib/base/{model.js → specialOptions.js} +16 -39
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/featureFlags.js +54 -24
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/onConditions.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/validator.js +10 -14
- package/lib/compiler/assert-consistency.js +11 -9
- package/lib/compiler/base.js +5 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +3 -3
- package/lib/compiler/define.js +6 -3
- package/lib/{base → compiler}/dictionaries.js +4 -3
- package/lib/compiler/extend.js +121 -21
- package/lib/compiler/generate.js +2 -2
- package/lib/compiler/index.js +11 -3
- package/lib/compiler/kick-start.js +1 -1
- package/lib/compiler/lsp-api.js +3 -3
- package/lib/compiler/populate.js +6 -7
- package/lib/compiler/resolve.js +53 -36
- package/lib/compiler/shared.js +68 -18
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +28 -27
- package/lib/compiler/xpr-rewrite.js +3 -3
- package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
- package/lib/edm/annotations/edmJson.js +2 -4
- package/lib/edm/annotations/genericTranslation.js +51 -7
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmAnnoPreprocessor.js +1 -1
- package/lib/edm/edmInboundChecks.js +2 -1
- package/lib/edm/edmPreprocessor.js +3 -3
- package/lib/edm/edmUtils.js +2 -2
- package/lib/gen/BaseParser.js +59 -108
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2052 -1965
- package/lib/gen/Dictionary.json +67 -7
- package/lib/json/from-csn.js +14 -14
- package/lib/json/to-csn.js +77 -38
- package/lib/main.js +3 -3
- package/lib/model/csnUtils.js +2 -2
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +1 -0
- package/lib/parsers/AstBuildingParser.js +83 -33
- package/lib/parsers/index.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +49 -30
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toSql.js +16 -7
- package/lib/render/utils/common.js +11 -3
- package/lib/render/utils/sql.js +14 -5
- package/lib/render/utils/standardDatabaseFunctions.js +108 -99
- package/lib/sql-identifier.js +9 -1
- package/lib/{model → tool-lib}/enrichCsn.js +3 -2
- package/lib/{model → tool-lib}/revealInternalProperties.js +2 -1
- package/lib/transform/addTenantFields.js +1 -1
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +1 -1
- package/lib/transform/db/backlinks.js +2 -2
- package/lib/transform/db/expansion.js +2 -2
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +2 -1
- package/lib/transform/db/rewriteCalculatedElements.js +2 -2
- package/lib/transform/db/temporal.js +30 -5
- package/lib/transform/db/views.js +16 -20
- package/lib/transform/draft/db.js +1 -2
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/flattening.js +6 -5
- package/lib/transform/effective/main.js +24 -4
- package/lib/transform/effective/types.js +1 -1
- package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
- package/lib/transform/forOdata.js +25 -7
- package/lib/transform/forRelationalDB.js +48 -12
- package/lib/transform/localized.js +2 -2
- package/lib/transform/odata/createForeignKeys.js +1 -1
- package/lib/transform/odata/flattening.js +2 -2
- package/lib/transform/odata/toFinalBaseType.js +3 -2
- package/lib/transform/odata/typesExposure.js +3 -2
- package/lib/transform/transformUtils.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +2 -1
- package/lib/transform/tupleExpansion.js +44 -4
- package/lib/transform/universalCsn/universalCsnEnricher.js +7 -3
- package/lib/transform/universalCsn/utils.js +1 -1
- package/lib/{base → utils}/lazyload.js +9 -0
- package/lib/{base → utils}/node-helpers.js +2 -0
- package/lib/utils/objectUtils.js +29 -6
- package/lib/{base → utils}/optionProcessorHelper.js +16 -6
- package/package.json +3 -40
- /package/lib/{model → base}/cloneCsn.js +0 -0
- /package/lib/{model/api.js → base/model-api.js} +0 -0
- /package/lib/{api → base}/trace.js +0 -0
- /package/lib/{model → base}/xprAsTree.js +0 -0
- /package/lib/{inspect → tool-lib}/index.js +0 -0
- /package/lib/{inspect → tool-lib}/inspectModelStatistics.js +0 -0
- /package/lib/{inspect → tool-lib}/inspectPropagation.js +0 -0
- /package/lib/{inspect → tool-lib}/inspectUtils.js +0 -0
- /package/lib/{base → utils}/shuffle.js +0 -0
package/lib/compiler/shared.js
CHANGED
|
@@ -121,7 +121,7 @@ function fns( model ) {
|
|
|
121
121
|
lexical: userBlock,
|
|
122
122
|
dynamic: modelBuiltinsOrDefinitions,
|
|
123
123
|
navigation: environment,
|
|
124
|
-
notFound:
|
|
124
|
+
notFound: undefinedFromReference,
|
|
125
125
|
accept: acceptQuerySource,
|
|
126
126
|
noDep: '', // dependency special for from
|
|
127
127
|
args: () => 'from-args',
|
|
@@ -131,7 +131,7 @@ function fns( model ) {
|
|
|
131
131
|
lexical: userBlock,
|
|
132
132
|
dynamic: modelBuiltinsOrDefinitions,
|
|
133
133
|
navigation: staticTarget,
|
|
134
|
-
notFound:
|
|
134
|
+
notFound: undefinedTypeReference,
|
|
135
135
|
accept: acceptTypeOrElement,
|
|
136
136
|
// special `scope`s for CDL parser - TYPE OF (TODO generated?), cds.Association:
|
|
137
137
|
typeOf: typeOfSemantics,
|
|
@@ -629,9 +629,11 @@ function fns( model ) {
|
|
|
629
629
|
|
|
630
630
|
if (Array.isArray( art ))
|
|
631
631
|
art = art[0];
|
|
632
|
-
if (!art)
|
|
633
|
-
return (semantics.dynamic !== modelDefinitions
|
|
634
|
-
|
|
632
|
+
if (!art) {
|
|
633
|
+
return (semantics.dynamic !== modelDefinitions && !options.$cdsReplSupport)
|
|
634
|
+
? art
|
|
635
|
+
: pathName( path );
|
|
636
|
+
}
|
|
635
637
|
const first = (art.kind === 'using' ? art.extern : art.name).id;
|
|
636
638
|
return (path.length === 1) ? first : `${ first }.${ pathName( ref.path.slice(1) ) }`;
|
|
637
639
|
}
|
|
@@ -788,6 +790,8 @@ function fns( model ) {
|
|
|
788
790
|
return undefined; // parse error
|
|
789
791
|
if (head._artifact !== undefined)
|
|
790
792
|
return head._artifact;
|
|
793
|
+
if (user.$extended && user._outer && !semantics.isMainRef)
|
|
794
|
+
user = user._outer;
|
|
791
795
|
let ruser = user._user || user; // TODO: nicer name if we keep this
|
|
792
796
|
// TODO: re-think _user link
|
|
793
797
|
if (ruser._outer && !semantics.isMainRef) {
|
|
@@ -1033,13 +1037,23 @@ function fns( model ) {
|
|
|
1033
1037
|
return null;
|
|
1034
1038
|
}
|
|
1035
1039
|
case 'mixin': {
|
|
1036
|
-
// use a source element having that name if in `extend … with columns
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1040
|
+
// use a source element having that name if in `extend … with` (columns or groupBy):
|
|
1041
|
+
const $extended = user._user?.$extended ?? user.$extended;
|
|
1042
|
+
const elem = $extended && art._parent._combined[head.id];
|
|
1039
1043
|
if (elem) {
|
|
1040
1044
|
path.$prefix = elem._parent.name.id; // prepend alias name
|
|
1041
|
-
|
|
1042
|
-
|
|
1045
|
+
if ($extended === 'columns') {
|
|
1046
|
+
warning( 'ref-special-in-extend', [ head.location, user ],
|
|
1047
|
+
{ '#': 'mixin', id: head.id, art: elem._origin._main } );
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
error( 'ref-unexpected-in-extend', [ head.location, user ], {
|
|
1051
|
+
'#': 'mixin',
|
|
1052
|
+
keyword: $extended,
|
|
1053
|
+
id: head.id,
|
|
1054
|
+
art: elem._origin._main,
|
|
1055
|
+
} );
|
|
1056
|
+
}
|
|
1043
1057
|
setLink( head, '_navigation', elem );
|
|
1044
1058
|
return setArtifactLink( head, elem._origin );
|
|
1045
1059
|
}
|
|
@@ -1050,21 +1064,30 @@ function fns( model ) {
|
|
|
1050
1064
|
return setArtifactLink( head, art._origin );
|
|
1051
1065
|
}
|
|
1052
1066
|
case '$tableAlias': {
|
|
1053
|
-
// use a source element having that name if in `extend … with columns
|
|
1054
|
-
const
|
|
1067
|
+
// use a source element having that name if in `extend … with` (columns or groupBy):
|
|
1068
|
+
const $extended = user._user?.$extended ?? user.$extended;
|
|
1055
1069
|
// if query source has duplicates, table alias has no elements
|
|
1056
1070
|
const elem = $extended && art.elements?.[head.id];
|
|
1057
1071
|
if (elem) {
|
|
1058
1072
|
path.$prefix = art.name.id; // prepend alias name
|
|
1059
|
-
|
|
1060
|
-
|
|
1073
|
+
if ($extended === 'columns') {
|
|
1074
|
+
warning( 'ref-special-in-extend', [ head.location, user ],
|
|
1075
|
+
{ '#': 'alias', id: head.id, art: elem._origin._main } );
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
error( 'ref-unexpected-in-extend', [ head.location, user ], {
|
|
1079
|
+
'#': 'alias',
|
|
1080
|
+
keyword: $extended,
|
|
1081
|
+
id: head.id,
|
|
1082
|
+
art: elem._origin._main,
|
|
1083
|
+
} );
|
|
1084
|
+
}
|
|
1061
1085
|
setLink( head, '_navigation', elem );
|
|
1062
1086
|
return setArtifactLink( head, elem._origin );
|
|
1063
1087
|
}
|
|
1064
1088
|
else if ($extended && art.elements) {
|
|
1065
|
-
warning( 'ref-deprecated-in-extend', [ head.location, user ],
|
|
1066
|
-
|
|
1067
|
-
'In an added column, do not use the table alias $(ID) to refer to source elements' );
|
|
1089
|
+
warning( 'ref-deprecated-in-extend', [ head.location, user ],
|
|
1090
|
+
{ '#': $extended, id: head.id } );
|
|
1068
1091
|
}
|
|
1069
1092
|
}
|
|
1070
1093
|
/* FALLTHROUGH */
|
|
@@ -1186,7 +1209,11 @@ function fns( model ) {
|
|
|
1186
1209
|
return model.definitions;
|
|
1187
1210
|
}
|
|
1188
1211
|
function modelBuiltinsOrDefinitions( user ) {
|
|
1189
|
-
|
|
1212
|
+
if (!definedViaCdl( user ))
|
|
1213
|
+
return model.definitions;
|
|
1214
|
+
if (!options.$cdsReplSupport)
|
|
1215
|
+
return model.$builtins;
|
|
1216
|
+
return Object.assign( { __proto__: null }, model.definitions, model.$builtins );
|
|
1190
1217
|
}
|
|
1191
1218
|
|
|
1192
1219
|
function artifactParams( user ) {
|
|
@@ -1355,6 +1382,21 @@ function fns( model ) {
|
|
|
1355
1382
|
// TODO: improve text, use text variant for: "or builtin" or "definitions" or none
|
|
1356
1383
|
}
|
|
1357
1384
|
|
|
1385
|
+
function undefinedFromReference( user, item, valid, dict, prev, path, semantics ) {
|
|
1386
|
+
const base = Functions.effectiveType?.( prev );
|
|
1387
|
+
if (Functions.effectiveType?.( base?.elements?.[item.id] )?.target)
|
|
1388
|
+
signalElementHint( user, item, valid, prev, semantics, path );
|
|
1389
|
+
else
|
|
1390
|
+
undefinedDefinition( user, item, valid, dict, prev, path, semantics );
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function undefinedTypeReference( user, item, valid, dict, prev, path, semantics ) {
|
|
1394
|
+
if (prev?.elements?.[item.id])
|
|
1395
|
+
signalElementHint( user, item, valid, prev, semantics, path );
|
|
1396
|
+
else
|
|
1397
|
+
undefinedDefinition( user, item, valid, dict, prev, path, semantics );
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1358
1400
|
function undefinedForAnnotate( user, item, valid, _dict, prev, path ) {
|
|
1359
1401
|
// in a CSN source, only one env was tested (valid.length 1):
|
|
1360
1402
|
const name = (prev) ? `${ prev.name.id }.${ item.id }` : item.id;
|
|
@@ -1382,6 +1424,14 @@ function fns( model ) {
|
|
|
1382
1424
|
}
|
|
1383
1425
|
}
|
|
1384
1426
|
|
|
1427
|
+
function signalElementHint( user, item, valid, prev, semantics, path ) {
|
|
1428
|
+
const art = searchName( prev, item.id, 'absolute' );
|
|
1429
|
+
const addSeparators = p => ( p === item ? ':' : '.' ) + p.id;
|
|
1430
|
+
const code = path[0].id + path.slice( 1 ).map( addSeparators ).join( '' );
|
|
1431
|
+
signalNotFound( 'ref-undefined-def', [ item.location, user ], valid,
|
|
1432
|
+
{ '#': 'hint', art, code }, semantics );
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1385
1435
|
function endsWithSuffix( name, suffix, cond ) {
|
|
1386
1436
|
if (!name.endsWith( suffix ))
|
|
1387
1437
|
return false;
|
|
@@ -87,7 +87,7 @@ function tweakAssocs( model ) {
|
|
|
87
87
|
forEachGeneric( art, 'elements', complainAboutTargetOutsideService );
|
|
88
88
|
|
|
89
89
|
if (art.query) {
|
|
90
|
-
traverseQueryPost(art.query,
|
|
90
|
+
traverseQueryPost(art.query, null, (query) => {
|
|
91
91
|
forEachGeneric( query, 'elements', handleQueryElements );
|
|
92
92
|
});
|
|
93
93
|
}
|
|
@@ -103,7 +103,7 @@ function tweakAssocs( model ) {
|
|
|
103
103
|
// Check explicit ON / keys with REDIRECTED TO
|
|
104
104
|
// TODO: run on all queries, but this is potentially incompatible
|
|
105
105
|
// function rewriteViewCheck( view ) {
|
|
106
|
-
// traverseQueryPost( view.query,
|
|
106
|
+
// traverseQueryPost( view.query, null, ( query ) => {
|
|
107
107
|
// forEachGeneric( query, 'elements', rewriteAssociationCheck );
|
|
108
108
|
// } );
|
|
109
109
|
// }
|
package/lib/compiler/utils.js
CHANGED
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
// Please do not add functions “for completeness”, this is not an API file for
|
|
7
7
|
// others but only by the core compiler.
|
|
8
8
|
|
|
9
|
-
// TODO: split this file into utils/….js, add some functions from lib/base/model.js
|
|
10
|
-
|
|
11
9
|
'use strict';
|
|
12
10
|
|
|
13
|
-
const { dictAdd, dictFirst } = require('
|
|
11
|
+
const { dictAdd, dictFirst } = require('./dictionaries');
|
|
14
12
|
const { Location, weakLocation } = require('../base/location');
|
|
15
13
|
const { XsnName, XsnArtifact } = require('./xsn-model');
|
|
16
14
|
|
|
@@ -294,6 +292,8 @@ const testFunctionPlaceholder = () => true;
|
|
|
294
292
|
* Return path step if the path navigates along an association whose final type
|
|
295
293
|
* satisfies function `test`; "navigates along" = last path item not considered
|
|
296
294
|
* without truthy optional argument `alsoTestLast`.
|
|
295
|
+
*
|
|
296
|
+
* This function assumes that the reference has already been resolved.
|
|
297
297
|
*/
|
|
298
298
|
function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
|
|
299
299
|
for (const item of ref.path || []) {
|
|
@@ -419,7 +419,7 @@ function targetMaxNotOne( assoc, item ) {
|
|
|
419
419
|
* two entities named `E`, the callback function is called on both.
|
|
420
420
|
* It is also called on columns with `inline`.
|
|
421
421
|
*
|
|
422
|
-
* See also function forEachDefinition()
|
|
422
|
+
* See also function forEachDefinition() below.
|
|
423
423
|
*/
|
|
424
424
|
function forEachUserArtifact( model, prop, callback ) { // not enums
|
|
425
425
|
forEachUserDict( model, prop, function main( art ) {
|
|
@@ -530,6 +530,11 @@ function forEachExprArray( query, array, refContext, exprContext, callback ) {
|
|
|
530
530
|
// i.e. is necessary to calculate the elements of the query
|
|
531
531
|
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
532
532
|
// NOTE: does not run on non-referred sub queries! Consider using ‹main›.$queries instead!
|
|
533
|
+
//
|
|
534
|
+
// Argument `simpleOnly`:
|
|
535
|
+
// - truthy: only simple queries + sub query in from = do not traverse join and union args
|
|
536
|
+
// - false, '', 0: all, but not right of union/intersect/etc
|
|
537
|
+
// - null, undefined: all, including right side of union/intersect/etc
|
|
533
538
|
function traverseQueryPost( query, simpleOnly, callback ) {
|
|
534
539
|
if (!query) // parser error
|
|
535
540
|
return;
|
|
@@ -540,7 +545,7 @@ function traverseQueryPost( query, simpleOnly, callback ) {
|
|
|
540
545
|
}
|
|
541
546
|
if (simpleOnly) {
|
|
542
547
|
const { from } = query;
|
|
543
|
-
if (!from || from.join) // parse error or join
|
|
548
|
+
if (!from || from.join) // parse error or join or union etc
|
|
544
549
|
return; // ok are: path or simple sub query (!)
|
|
545
550
|
}
|
|
546
551
|
if (query.from) { // SELECT
|
|
@@ -550,7 +555,7 @@ function traverseQueryPost( query, simpleOnly, callback ) {
|
|
|
550
555
|
// console.log('FE:')
|
|
551
556
|
}
|
|
552
557
|
else if (query.args) { // JOIN, UNION, INTERSECT
|
|
553
|
-
if (!query.join && simpleOnly
|
|
558
|
+
if (!query.join && simpleOnly != null) {
|
|
554
559
|
// enough for elements: traverse only first args for UNION/INTERSECT
|
|
555
560
|
// TODO: we might use this also when we do not rewrite associations
|
|
556
561
|
// in non-referred sub queries
|
|
@@ -578,7 +583,7 @@ function traverseQueryExtra( main, callback ) {
|
|
|
578
583
|
if (!main.$queries)
|
|
579
584
|
return;
|
|
580
585
|
// with a top-level UNION, $queries[0] is just the left
|
|
581
|
-
traverseQueryPost( main.query,
|
|
586
|
+
traverseQueryPost( main.query, null, (q) => { // also into right of UNION (to be compatible)
|
|
582
587
|
setLink( q, '_status', 'extra' );
|
|
583
588
|
callback( q );
|
|
584
589
|
} );
|
|
@@ -586,7 +591,7 @@ function traverseQueryExtra( main, callback ) {
|
|
|
586
591
|
if (query._status === 'extra' || query._parent.kind === '$tableAlias')
|
|
587
592
|
continue; // if parent is alias, query is FROM source -> run by traverseQueryPost
|
|
588
593
|
// we are now in the top-level (parent is entity) or a non-referred query (parent is query)
|
|
589
|
-
traverseQueryPost( query,
|
|
594
|
+
traverseQueryPost( query, false, callback ); // not into right of UNION
|
|
590
595
|
}
|
|
591
596
|
}
|
|
592
597
|
|
|
@@ -604,15 +609,18 @@ function viewFromPrimary( view ) {
|
|
|
604
609
|
/**
|
|
605
610
|
* About Helper property $expand for faster the XSN-to-CSN transformation
|
|
606
611
|
* - null/undefined: artifact, member, items does not contain expanded members
|
|
607
|
-
* - 'origin': all expanded (sub) elements have no
|
|
612
|
+
* - 'origin': all expanded (sub) elements have no associations and no new annotations
|
|
608
613
|
* that value is only on elements, types, and params -> no other members
|
|
609
614
|
* when set, only on elem/art with expanded elements
|
|
610
|
-
* - 'target': all expanded (sub) elements might only have
|
|
615
|
+
* - 'target': all expanded (sub) elements might only have associations, but
|
|
611
616
|
* no individual annotations on any (sub) member
|
|
612
617
|
* when set, traverse all parents where the value has been 'origin' before
|
|
618
|
+
* TODO: only set if `target` or `on` has changed during redirection/rewrite
|
|
613
619
|
* - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
614
620
|
* not counting propagated ones; set up to the definition (main artifact)
|
|
615
|
-
* (only set with anno on $inferred elem), annotate
|
|
621
|
+
* (only set with anno on $inferred elem), 'annotate' beats 'target'
|
|
622
|
+
* - 'extend': at least one member has been added, 'extend' beats 'annotate'
|
|
623
|
+
*
|
|
616
624
|
* Usage according to CSN flavor:
|
|
617
625
|
* - gensrc: do not render inferred elements (including expanded elements),
|
|
618
626
|
* collect annotate statements with value 'annotate'
|
|
@@ -621,22 +629,16 @@ function viewFromPrimary( view ) {
|
|
|
621
629
|
* (might sometimes render the elements unnecessarily, which is not wrong)
|
|
622
630
|
* - universal: do not render expanded sub elements if $expand = 'origin'
|
|
623
631
|
*/
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
elem.$expand = status; // meaning: expanded, containing assocs
|
|
631
|
-
for (let line = elem.items; line; line = line.items)
|
|
632
|
-
line.$expand = status; // to-csn just uses the innermost $expand
|
|
633
|
-
}
|
|
634
|
-
}
|
|
632
|
+
const expandStatusLevel = {
|
|
633
|
+
origin: 1,
|
|
634
|
+
target: 2,
|
|
635
|
+
annotate: 3,
|
|
636
|
+
extend: 4,
|
|
637
|
+
};
|
|
635
638
|
|
|
636
|
-
function
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
return; // already set
|
|
639
|
+
function setExpandStatus( elem, status ) {
|
|
640
|
+
const statusLevel = expandStatusLevel[status];
|
|
641
|
+
while (!elem.$expand || expandStatusLevel[elem.$expand] < statusLevel) {
|
|
640
642
|
elem.$expand = status; // meaning: expanded, containing annos
|
|
641
643
|
for (let line = elem.items; line; line = line.items)
|
|
642
644
|
line.$expand = status; // to-csn just uses the innermost $expand
|
|
@@ -864,7 +866,6 @@ module.exports = {
|
|
|
864
866
|
traverseQueryExtra,
|
|
865
867
|
viewFromPrimary,
|
|
866
868
|
setExpandStatus,
|
|
867
|
-
setExpandStatusAnnotate,
|
|
868
869
|
isDirectComposition,
|
|
869
870
|
targetCantBeAspect,
|
|
870
871
|
userQuery,
|
|
@@ -148,10 +148,10 @@ const { weakLocation } = require('../base/location');
|
|
|
148
148
|
const {
|
|
149
149
|
setArtifactLink,
|
|
150
150
|
setLink,
|
|
151
|
-
|
|
151
|
+
setExpandStatus,
|
|
152
152
|
} = require('./utils');
|
|
153
153
|
const { CompilerAssertion } = require('../base/error');
|
|
154
|
-
const { isBetaEnabled } = require('../base/
|
|
154
|
+
const { isBetaEnabled } = require('../base/specialOptions');
|
|
155
155
|
const { isSimpleCdlIdentifier } = require('../parsers/identifiers');
|
|
156
156
|
|
|
157
157
|
// Config object passed around all "rewrite" functions.
|
|
@@ -550,7 +550,7 @@ function xprRewriteFns( model ) {
|
|
|
550
550
|
|
|
551
551
|
stripAbsolutePathPrefix( expr, config.expandedRootType );
|
|
552
552
|
prependRootPath( config.expandedRootType, config.expandedRoot, expr );
|
|
553
|
-
|
|
553
|
+
setExpandStatus( config.destination, 'annotate' );
|
|
554
554
|
|
|
555
555
|
config.destination[config.anno].$inferred = 'anno-rewrite';
|
|
556
556
|
// $self-paths via type expansion always need to be rewritten.
|
|
@@ -244,7 +244,10 @@ const EdmPrimitiveTypeMap = {
|
|
|
244
244
|
exact: 0,
|
|
245
245
|
desc: 'Abstract meta type',
|
|
246
246
|
},
|
|
247
|
-
|
|
247
|
+
'Edm.Untyped': {
|
|
248
|
+
v4: true,
|
|
249
|
+
desc: 'Abstract void type',
|
|
250
|
+
},
|
|
248
251
|
};
|
|
249
252
|
const EdmPathTypeMap = {
|
|
250
253
|
'Edm.AnnotationPath': 1,
|
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
} = require('../EdmPrimitiveTypeDefinitions.js');
|
|
9
9
|
const { isBuiltinType, isAnnotationExpression } = require('../../base/builtins');
|
|
10
10
|
const { transformExpression } = require('../../transform/db/applyTransformations.js');
|
|
11
|
-
const { conditionAsTree, expressionAsTree } = require('../../
|
|
11
|
+
const { conditionAsTree, expressionAsTree } = require('../../base/xprAsTree');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Translate a given token stream expression into an edmJson representation
|
|
@@ -390,9 +390,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions, generi
|
|
|
390
390
|
|
|
391
391
|
|
|
392
392
|
transform.ref = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
393
|
-
|
|
394
|
-
// we ignore the filters in order to generate EDMX
|
|
395
|
-
if (xpr.some(ps => ps.args/* || ps.where */)) {
|
|
393
|
+
if (xpr.some(ps => ps.args /* || (ps.where && ps.where.length)) until PR#13752 filters were also forbidden */)) {
|
|
396
394
|
error('odata-anno-xpr-ref', location, {
|
|
397
395
|
anno, elemref: parent, '#': 'args',
|
|
398
396
|
});
|
|
@@ -6,7 +6,8 @@ const edmUtils = require('../edmUtils.js');
|
|
|
6
6
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
7
7
|
const preprocessAnnotations = require('./preprocessAnnotations.js');
|
|
8
8
|
const { forEachDefinition } = require('../../model/csnUtils');
|
|
9
|
-
const { isBetaEnabled
|
|
9
|
+
const { isBetaEnabled } = require('../../base/specialOptions.js');
|
|
10
|
+
const { setProp } = require('../../utils/objectUtils.js');
|
|
10
11
|
const { xpr2edmJson, getEdmJsonHandler } = require('./edmJson.js');
|
|
11
12
|
const { vocabularyDefinitions } = require('./vocabularyDefinitions.js');
|
|
12
13
|
const { EdmPathTypeMap } = require('../EdmPrimitiveTypeDefinitions.js');
|
|
@@ -890,13 +891,18 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
890
891
|
// oTarget: the result object (o: odata)
|
|
891
892
|
// oTermName: current term
|
|
892
893
|
// dTypeNameArg: expected type of cAnnoValue according to dictionary, may be null (d: dictionary)
|
|
893
|
-
|
|
894
|
+
// dTypeDef: the whole object definition of the typefrom the dictionary, needed for DerivedTypeConstraints for Edm.Untyped
|
|
895
|
+
// @ts-ignore
|
|
896
|
+
function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg/* , dTypeDef = undefined */ ) {
|
|
894
897
|
// this function basically only figures out what kind of annotation value we have
|
|
895
898
|
// (can be: array, expression, enum, pseudo-record, record, simple value),
|
|
896
899
|
// then calls a more specific function to deal with it and puts
|
|
897
900
|
// the result into the oTarget object
|
|
898
901
|
|
|
899
|
-
const [ dTypeName, dTypeIsACollection ] =
|
|
902
|
+
const [ dTypeName, dTypeIsACollection/* , dTypeIsUntyped */ ] = stripCollectionAndgetIsCollectionOrUntyped(dTypeNameArg);
|
|
903
|
+
|
|
904
|
+
// if (dTypeIsUntyped)
|
|
905
|
+
// validateUntypedAnnotationValue(cAnnoValue, typeof dTypeNameArg === 'object' ? dTypeNameArg : dTypeDef, msg);
|
|
900
906
|
|
|
901
907
|
if (Array.isArray(cAnnoValue)) {
|
|
902
908
|
if (isEnumType(dTypeName)) {
|
|
@@ -914,7 +920,12 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
914
920
|
if ('=' in cAnnoValue) {
|
|
915
921
|
if (dTypeIsACollection) {
|
|
916
922
|
message('odata-anno-value', msg.location,
|
|
917
|
-
{
|
|
923
|
+
{
|
|
924
|
+
anno: msg.anno(),
|
|
925
|
+
type: dTypeName,
|
|
926
|
+
str: 'path',
|
|
927
|
+
'#': 'incompval',
|
|
928
|
+
});
|
|
918
929
|
}
|
|
919
930
|
// expression
|
|
920
931
|
const res = handleExpression(cAnnoValue['='], dTypeName);
|
|
@@ -1080,6 +1091,30 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1080
1091
|
// concatenate all the enums to a string, separated by spaces
|
|
1081
1092
|
return cAnnoValue.filter( x => x['#']).map( x => (forXml ? `${ dTypeName }/` : '') + x['#'] ).join(forXml ? ' ' : ',');
|
|
1082
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
// --------------------- Edm.Untyped helper function ----------------------
|
|
1096
|
+
|
|
1097
|
+
// // eslint-disable-next-line no-shadow
|
|
1098
|
+
// function validateUntypedAnnotationValue( cAnnoValue, dTypeDef, msg ) {
|
|
1099
|
+
// if (!dTypeDef?.DerivedTypeConstraint)
|
|
1100
|
+
// return;
|
|
1101
|
+
// // if the annotation value is a path, but no path is allowed for this annotation, issue a message,
|
|
1102
|
+
// // for example the UI.DataField.Value annotation
|
|
1103
|
+
// if (cAnnoValue.$edmJson?.$Path/* || cAnnoValue['=']) -> this is handled in handleValue */ &&
|
|
1104
|
+
// !(dTypeDef.DerivedTypeConstraint.some(type => EdmPathTypeMap[type]) ||
|
|
1105
|
+
// dTypeDef.DerivedTypeConstraint.includes('Edm.EntityType') ||
|
|
1106
|
+
// dTypeDef.DerivedTypeConstraint.includes('Edm.ComplexType'))
|
|
1107
|
+
// ) {
|
|
1108
|
+
// message('odata-anno-value', msg.location,
|
|
1109
|
+
// {
|
|
1110
|
+
// anno: msg.anno(),
|
|
1111
|
+
// str: 'path',
|
|
1112
|
+
// type: 'Edm.Untyped',
|
|
1113
|
+
// '#': 'incompval',
|
|
1114
|
+
// });
|
|
1115
|
+
// }
|
|
1116
|
+
// }
|
|
1117
|
+
// ----------------------------------------------------------------------
|
|
1083
1118
|
}
|
|
1084
1119
|
|
|
1085
1120
|
// found an expression value ("=") "expr"
|
|
@@ -1417,7 +1452,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1417
1452
|
function generateCollection( annoValue, termName, dTypeName, dTypeIsACollection, msg ) {
|
|
1418
1453
|
const newCollection = new Edm.Collection(v);
|
|
1419
1454
|
|
|
1420
|
-
if (dTypeName && !dTypeIsACollection) {
|
|
1455
|
+
if (dTypeName && !dTypeIsACollection && dTypeName !== 'Edm.Untyped') {
|
|
1421
1456
|
message('odata-anno-value', msg.location,
|
|
1422
1457
|
{
|
|
1423
1458
|
anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'incompval',
|
|
@@ -1720,14 +1755,23 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1720
1755
|
return dTypeName;
|
|
1721
1756
|
}
|
|
1722
1757
|
|
|
1723
|
-
function
|
|
1724
|
-
if (typeName) {
|
|
1758
|
+
function stripCollectionAndgetIsCollectionOrUntyped( typeName ) {
|
|
1759
|
+
if (typeName && typeof typeName === 'string') {
|
|
1725
1760
|
const match = typeName.match(/^Collection\((.+)\)/);
|
|
1726
1761
|
if (match)
|
|
1727
1762
|
return [ match[1], true ];
|
|
1763
|
+
// return [ match[1], true, match[1] === 'Edm.Untyped' ];
|
|
1728
1764
|
}
|
|
1729
1765
|
|
|
1766
|
+
// in the Dictionary.json, if a certain element or type has type 'Edm.Untyped', we also store
|
|
1767
|
+
// a 'DerivedTypeConstraint' property containing the possible types for this element or type.
|
|
1768
|
+
// Therefore, the type is not just a string but an object: { Type: 'Edm.Untyped', DerivedTypeConstraint: [...] }
|
|
1769
|
+
if (typeName && typeof typeName === 'object')
|
|
1770
|
+
return [ typeName.Type, typeName.DerivedTypeConstraint.every(type => type.startsWith('Collection(')) ];
|
|
1771
|
+
// return [ typeName.Type, typeName.DerivedTypeConstraint.some(type => type.startsWith('Collection(')), true ];
|
|
1772
|
+
|
|
1730
1773
|
return [ typeName, false ];
|
|
1774
|
+
// return [ typeName, false, typeName === 'Edm.Untyped' ];
|
|
1731
1775
|
}
|
|
1732
1776
|
|
|
1733
1777
|
function isPrimitiveType( typeName ) {
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -9,7 +9,8 @@ const VALUELIST_NAVPROP_PREFIX = '';
|
|
|
9
9
|
const edmUtils = require('./edmUtils.js');
|
|
10
10
|
const { initializeModel } = require('./edmPreprocessor.js');
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
|
-
const { setProp
|
|
12
|
+
const { setProp } = require('../utils/objectUtils');
|
|
13
|
+
const { isBetaEnabled } = require('../base/specialOptions');
|
|
13
14
|
const {
|
|
14
15
|
isEdmPropertyRendered, getUtils, findAnnotationExpression,
|
|
15
16
|
} = require('../model/csnUtils');
|
|
@@ -21,7 +22,7 @@ const {
|
|
|
21
22
|
EdmPrimitiveTypeMap,
|
|
22
23
|
} = require('./EdmPrimitiveTypeDefinitions.js');
|
|
23
24
|
const { getEdm } = require('./edm.js');
|
|
24
|
-
const { cloneFullCsn } = require('../
|
|
25
|
+
const { cloneFullCsn } = require('../base/cloneCsn');
|
|
25
26
|
const { forEach, forEachValue } = require('../utils/objectUtils.js');
|
|
26
27
|
/*
|
|
27
28
|
OData V2 spec 06/01/2017 PDF version is available here:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp
|
|
3
|
+
const { setProp } = require('../utils/objectUtils');
|
|
4
|
+
const { isBetaEnabled } = require('../base/specialOptions');
|
|
4
5
|
const {
|
|
5
6
|
forEachDefinition, forEachMemberRecursively, getUtils,
|
|
6
7
|
transformAnnotationExpression,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/* eslint max-statements-per-line:off */
|
|
4
|
-
const {
|
|
4
|
+
const { isBetaEnabled } = require('../base/specialOptions');
|
|
5
5
|
const {
|
|
6
6
|
forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
7
7
|
isEdmPropertyRendered, getUtils,
|
|
@@ -15,8 +15,8 @@ const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
|
|
|
15
15
|
const { inboundQualificationChecks } = require('./edmInboundChecks.js');
|
|
16
16
|
const typesExposure = require('../transform/odata/typesExposure');
|
|
17
17
|
const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
|
|
18
|
-
const { cloneCsnNonDict, cloneAnnotationValue } = require('../
|
|
19
|
-
const { forEach, forEachKey } = require('../utils/objectUtils.js');
|
|
18
|
+
const { cloneCsnNonDict, cloneAnnotationValue } = require('../base/cloneCsn');
|
|
19
|
+
const { setProp, forEach, forEachKey } = require('../utils/objectUtils.js');
|
|
20
20
|
|
|
21
21
|
const NavResAnno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
|
|
22
22
|
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp } = require('../
|
|
3
|
+
const { setProp } = require('../utils/objectUtils');
|
|
4
4
|
const {
|
|
5
5
|
isEdmPropertyRendered, applyTransformations,
|
|
6
6
|
} = require('../model/csnUtils');
|
|
7
7
|
const { isBuiltinType } = require('../base/builtins');
|
|
8
8
|
const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
|
|
9
9
|
const { CompilerAssertion } = require('../base/error');
|
|
10
|
-
const { cloneAnnotationValue } = require('../
|
|
10
|
+
const { cloneAnnotationValue } = require('../base/cloneCsn');
|
|
11
11
|
|
|
12
12
|
/* eslint max-statements-per-line:off */
|
|
13
13
|
function validateOptions( _options ) {
|