@sap/cds-compiler 6.6.2 → 6.7.2
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 +37 -1
- package/bin/cdsc.js +2 -0
- package/bin/cdsse.js +1 -1
- package/lib/base/message-registry.js +13 -7
- package/lib/base/model.js +0 -72
- package/lib/checks/elements.js +1 -1
- package/lib/checks/featureFlags.js +2 -2
- package/lib/checks/manyExpand.js +28 -0
- package/lib/checks/validator.js +2 -0
- package/lib/compiler/assert-consistency.js +3 -4
- package/lib/compiler/base.js +8 -0
- package/lib/compiler/builtins.js +8 -9
- package/lib/compiler/checks.js +27 -6
- package/lib/compiler/cycle-detector.js +4 -4
- package/lib/compiler/define.js +65 -83
- package/lib/compiler/extend.js +357 -325
- package/lib/compiler/finalize-parse-cdl.js +3 -4
- package/lib/compiler/generate.js +205 -203
- package/lib/compiler/kick-start.js +34 -49
- package/lib/compiler/populate.js +95 -28
- package/lib/compiler/propagator.js +3 -5
- package/lib/compiler/resolve.js +17 -13
- package/lib/compiler/shared.js +52 -20
- package/lib/compiler/tweak-assocs.js +2 -4
- package/lib/compiler/utils.js +84 -31
- package/lib/edm/edmAnnoPreprocessor.js +2 -2
- package/lib/gen/BaseParser.js +924 -1055
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +5 -2
- package/lib/json/from-csn.js +25 -16
- package/lib/main.d.ts +13 -0
- package/lib/model/revealInternalProperties.js +18 -0
- package/lib/parsers/AstBuildingParser.js +22 -5
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toSql.js +1 -1
- package/lib/render/utils/sql.js +2 -2
- package/lib/render/utils/standardDatabaseFunctions.js +2 -2
- package/lib/transform/db/constraints.js +3 -4
- package/lib/transform/db/expansion.js +2 -44
- package/lib/transform/db/killAnnotations.js +1 -1
- package/lib/transform/db/processSqlServices.js +10 -11
- package/lib/transform/effective/main.js +2 -1
- package/lib/transform/featureFlags.js +6 -1
- package/lib/transform/forOdata.js +7 -124
- package/lib/transform/forRelationalDB.js +2 -1
- package/lib/transform/odata/fioriTreeViews.js +173 -0
- package/lib/transform/odata/flattening.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +7 -4
- package/package.json +1 -1
- package/share/messages/message-explanations.json +0 -2
- package/share/messages/type-unexpected-foreign-keys.md +0 -52
- package/share/messages/type-unexpected-on-condition.md +0 -52
package/lib/compiler/shared.js
CHANGED
|
@@ -43,11 +43,15 @@ function fns( model ) {
|
|
|
43
43
|
// Map `exprCtx` (is a param of traversal functions) to reference semantics
|
|
44
44
|
const referenceSemantics = {
|
|
45
45
|
// global: ------------------------------------------------------------------
|
|
46
|
-
using: { // only used to produce
|
|
47
|
-
isMainRef: '
|
|
46
|
+
using: { // only used to produce warnings
|
|
47
|
+
isMainRef: 'no-leaf-gap',
|
|
48
48
|
lexical: null,
|
|
49
49
|
dynamic: modelDefinitions,
|
|
50
50
|
notFound: undefinedDefinition,
|
|
51
|
+
messageMap: {
|
|
52
|
+
'ref-undefined-art': 'ref-undefined-using',
|
|
53
|
+
'ref-undefined-def': 'ref-undefined-using',
|
|
54
|
+
},
|
|
51
55
|
},
|
|
52
56
|
// scope:'global': for cds.Association and auto-redirected targets
|
|
53
57
|
$global: {
|
|
@@ -58,14 +62,14 @@ function fns( model ) {
|
|
|
58
62
|
},
|
|
59
63
|
// only used for the main annotate/extend statements, not inner ones:
|
|
60
64
|
annotate: {
|
|
61
|
-
isMainRef: '
|
|
65
|
+
isMainRef: 'no-leaf-gap',
|
|
62
66
|
lexical: userBlock,
|
|
63
67
|
dynamic: modelDefinitions,
|
|
64
68
|
notFound: undefinedForAnnotate,
|
|
65
69
|
accept: extendableArtifact,
|
|
66
70
|
},
|
|
67
71
|
'annotate-sec': {
|
|
68
|
-
isMainRef: '
|
|
72
|
+
isMainRef: 'no-leaf-gap',
|
|
69
73
|
lexical: userBlock,
|
|
70
74
|
dynamic: modelDefinitions,
|
|
71
75
|
notFound: undefinedDefinition,
|
|
@@ -660,7 +664,6 @@ function fns( model ) {
|
|
|
660
664
|
if (!root)
|
|
661
665
|
return setArtifactLink( ref, root );
|
|
662
666
|
|
|
663
|
-
// how many path items are for artifacts (rest: elements)
|
|
664
667
|
let art = getPathItem( ref, semantics, user );
|
|
665
668
|
if (!art)
|
|
666
669
|
return setArtifactLink( ref, art );
|
|
@@ -838,16 +841,19 @@ function fns( model ) {
|
|
|
838
841
|
const dynamicDict = semantics.dynamic( ruser, user._user && user._artifact );
|
|
839
842
|
if (!dynamicDict) // avoid consequential errors
|
|
840
843
|
return setArtifactLink( head, null );
|
|
844
|
+
|
|
841
845
|
const isVar = (semantics.dollar && head.id.charAt( 0 ) === '$');
|
|
842
846
|
const dict = (isVar) ? model.$magicVariables.elements : dynamicDict;
|
|
843
|
-
const r = dict[head.id]
|
|
847
|
+
const r = dict[head.id] || // or a ref like "MyEntity.texts" in CSN
|
|
848
|
+
isMainRef && Functions.generateOnDemand?.( head.id );
|
|
844
849
|
if (r)
|
|
845
850
|
return setArtifactLink( head, r );
|
|
846
851
|
|
|
847
852
|
if (!semantics.dollar) {
|
|
848
853
|
valid.push( dynamicDict );
|
|
854
|
+
const noGen = !definedViaCdl( ruser );
|
|
849
855
|
if (isMainRef) // eslint-disable-next-line no-return-assign
|
|
850
|
-
valid.forEach( ( d, idx ) => (valid[idx] = removeGapArtifact( d )) );
|
|
856
|
+
valid.forEach( ( d, idx ) => (valid[idx] = removeGapArtifact( d, noGen )) );
|
|
851
857
|
}
|
|
852
858
|
else {
|
|
853
859
|
const filterFn = semantics.variableFilter || removeRestrictedVariables;
|
|
@@ -871,6 +877,7 @@ function fns( model ) {
|
|
|
871
877
|
function getPathItem( ref, semantics, user ) {
|
|
872
878
|
// let art = (headArt && headArt.kind === '$tableAlias') ? headArt._origin : headArt;
|
|
873
879
|
const { path } = ref;
|
|
880
|
+
// how many path items are for artifacts (the rest are for elements):
|
|
874
881
|
let artItemsCount = 0;
|
|
875
882
|
const { isMainRef } = semantics;
|
|
876
883
|
if (isMainRef) {
|
|
@@ -894,7 +901,10 @@ function fns( model ) {
|
|
|
894
901
|
const envFn = (artItemsCount >= 0) ? artifactsEnv : elementsEnv;
|
|
895
902
|
// TOOD: call envFn with location of last item (for dependency error)
|
|
896
903
|
const env = envFn( art, path[index - 1].location, user );
|
|
897
|
-
|
|
904
|
+
let found = env && env[item.id]; // not env?.[…] - keep a `0`
|
|
905
|
+
if (!found && found !== 0 && artItemsCount >= 0)
|
|
906
|
+
found = Functions.generateOnDemand?.( `${ prev.name.id }.${ item.id }` );
|
|
907
|
+
|
|
898
908
|
// Reject `$self.$_column_1`: TODO: necessary to do here again?
|
|
899
909
|
art = setArtifactLink( item, (found?.name?.$inferred === '$internal') ? undefined : found );
|
|
900
910
|
|
|
@@ -904,24 +914,33 @@ function fns( model ) {
|
|
|
904
914
|
const notFound = (artItemsCount >= 0) ? semantics.notFound : undefinedItemElement;
|
|
905
915
|
// TODO: streamline function arguments (probably: user, path, semantics, prev )
|
|
906
916
|
// false returned by semantics.navigation: no further error:
|
|
907
|
-
if (env !== false)
|
|
908
|
-
|
|
917
|
+
if (env !== false) {
|
|
918
|
+
const exp = (artItemsCount >= 0)
|
|
919
|
+
? removeGapArtifact( env, !definedViaCdl( user ) )
|
|
920
|
+
: env;
|
|
921
|
+
notFound( user, item, [ exp ], null, prev, path, semantics );
|
|
922
|
+
}
|
|
909
923
|
return null;
|
|
910
924
|
}
|
|
911
925
|
// need to do that here, because we also need to disallow Service.AutoExposed:elem
|
|
912
926
|
// TODO: but Service.AutoExposed.NotAuto should be fine
|
|
913
927
|
if (isMainRef && isMainRef !== 'all' && artItemsCount === 0) {
|
|
914
928
|
if (art.kind === 'namespace') {
|
|
929
|
+
if (Functions.generateOnDemand?.( art ) ||
|
|
930
|
+
isMainRef === 'no-leaf-gap' && art._subArtifacts)
|
|
931
|
+
continue; // art is generated entity assigned to gap object
|
|
915
932
|
if (env !== false) {
|
|
916
|
-
semantics.notFound( user, item, [ removeGapArtifact( env ) ],
|
|
933
|
+
semantics.notFound( user, item, [ removeGapArtifact( env, !definedViaCdl( user ) ) ],
|
|
917
934
|
null, prev, path, semantics );
|
|
918
935
|
}
|
|
919
936
|
return null;
|
|
920
937
|
}
|
|
921
|
-
else if (art.$inferred === 'autoexposed' && !user.$inferred
|
|
938
|
+
else if (art.$inferred === 'autoexposed' && !user.$inferred &&
|
|
939
|
+
isMainRef !== 'no-leaf-gap') {
|
|
922
940
|
// Depending on the processing sequence, the following could be a
|
|
923
941
|
// simple 'ref-undefined-art'/'ref-undefined-def' - TODO: which we
|
|
924
942
|
// could "change" to this message at the end of compile():
|
|
943
|
+
// HINT: this does not appear anymore
|
|
925
944
|
error( 'ref-unexpected-autoexposed', [ item.location, user ], { art },
|
|
926
945
|
'An auto-exposed entity can\'t be referred to - expose entity $(ART) explicitly' );
|
|
927
946
|
return null; // continuation semantics: like “not found”
|
|
@@ -983,9 +1002,8 @@ function fns( model ) {
|
|
|
983
1002
|
|
|
984
1003
|
switch (art.kind) {
|
|
985
1004
|
case 'using': {
|
|
986
|
-
const def = model.definitions[art.extern.id]
|
|
987
|
-
|
|
988
|
-
return def;
|
|
1005
|
+
const def = model.definitions[art.extern.id] ||
|
|
1006
|
+
Functions.createGapArtifact( art.extern.id, art.extern.location );
|
|
989
1007
|
if (def.$duplicates)
|
|
990
1008
|
return false;
|
|
991
1009
|
art = setArtifactLink( head, def ); // we do not want to see the using
|
|
@@ -994,15 +1012,21 @@ function fns( model ) {
|
|
|
994
1012
|
}
|
|
995
1013
|
/* FALLTHROUGH */
|
|
996
1014
|
case 'namespace': {
|
|
997
|
-
if (semantics.isMainRef === 'all' ||
|
|
1015
|
+
if (semantics.isMainRef === 'all' ||
|
|
1016
|
+
semantics.isMainRef === 'no-leaf-gap' && art._subArtifacts ||
|
|
1017
|
+
path.length !== 1 && ref.scope !== 1 ||
|
|
1018
|
+
// "MyEntity.texts" in CSN or via `using` which is (at the moment)
|
|
1019
|
+
// just a gap artifact
|
|
1020
|
+
Functions.generateOnDemand?.( art ))
|
|
998
1021
|
return art;
|
|
1022
|
+
|
|
999
1023
|
const valid = [];
|
|
1000
1024
|
const lexical = userBlock( user );
|
|
1001
1025
|
if (lexical) {
|
|
1002
1026
|
for (let env = lexical; env; env = env._block)
|
|
1003
1027
|
valid.push( removeGapArtifact( env.artifacts || Object.create( null ) ) );
|
|
1004
1028
|
}
|
|
1005
|
-
valid.push( removeGapArtifact( model.definitions ) );
|
|
1029
|
+
valid.push( removeGapArtifact( model.definitions, !definedViaCdl( user ) ) );
|
|
1006
1030
|
semantics.notFound?.( user._user || user, head, valid, model.definitions,
|
|
1007
1031
|
null, path, semantics );
|
|
1008
1032
|
|
|
@@ -1488,7 +1512,7 @@ function fns( model ) {
|
|
|
1488
1512
|
}
|
|
1489
1513
|
else {
|
|
1490
1514
|
const target = art._effectiveType?.target;
|
|
1491
|
-
if (target
|
|
1515
|
+
if (resolvePath( target, 'target', art )) {
|
|
1492
1516
|
signalNotFound( 'ref-undefined-element', [ item.location, user ], valid,
|
|
1493
1517
|
{ '#': 'target', art: target, id: item.id }, semantics );
|
|
1494
1518
|
}
|
|
@@ -1622,6 +1646,9 @@ function fns( model ) {
|
|
|
1622
1646
|
}
|
|
1623
1647
|
|
|
1624
1648
|
function acceptStructOrBare( art, user, ref ) { // for includes[]
|
|
1649
|
+
// eslint-disable-next-line @stylistic/object-curly-newline
|
|
1650
|
+
if (user.kind in { context: 1, service: 1, action: 1, function: 1 })
|
|
1651
|
+
return false;
|
|
1625
1652
|
// It had been checked before that `includes` is already forbidden for
|
|
1626
1653
|
// non-entity/aspect/type/event.
|
|
1627
1654
|
//
|
|
@@ -1650,6 +1677,9 @@ function fns( model ) {
|
|
|
1650
1677
|
// We might allow includes with elements in the future, they'd probably
|
|
1651
1678
|
// count as specified elements with lower priority, i.e. annos, types, key
|
|
1652
1679
|
// etc on columns beat those inherited from the include.
|
|
1680
|
+
// TODO v7 forbid `extend Proj with ZeroElemAspect` even if ZeroElemAspect has
|
|
1681
|
+
// 0 elements. Making the element-length check work requires that effectiveType()
|
|
1682
|
+
// has been called on the include already before.
|
|
1653
1683
|
if (art.kind === 'aspect' &&
|
|
1654
1684
|
(!art.elements || base.query && !Object.keys( art.elements ).length))
|
|
1655
1685
|
return art;
|
|
@@ -2181,12 +2211,14 @@ function removeDollarNames( dict ) {
|
|
|
2181
2211
|
return r;
|
|
2182
2212
|
}
|
|
2183
2213
|
|
|
2184
|
-
function removeGapArtifact( dict ) {
|
|
2214
|
+
function removeGapArtifact( dict, alsoRemoveInferred ) {
|
|
2185
2215
|
const r = Object.create( null );
|
|
2186
2216
|
for (const name in dict) {
|
|
2187
2217
|
const art = dict[name];
|
|
2188
|
-
// TODO: for gaps with sub artifacts,
|
|
2218
|
+
// TODO: for gaps with sub artifacts, use `${name}.` as name
|
|
2189
2219
|
// TODO: clarify with LSP
|
|
2220
|
+
if (alsoRemoveInferred && art.$inferred)
|
|
2221
|
+
continue;
|
|
2190
2222
|
if (art.kind !== 'namespace' || art._subArtifacts)
|
|
2191
2223
|
r[name] = dict[name];
|
|
2192
2224
|
}
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const {
|
|
6
|
-
forEachGeneric,
|
|
7
|
-
forEachInOrder,
|
|
8
|
-
} = require('../base/model');
|
|
9
5
|
const { dictLocation, weakLocation, weakRefLocation } = require('../base/location');
|
|
10
6
|
|
|
11
7
|
const {
|
|
@@ -19,6 +15,8 @@ const {
|
|
|
19
15
|
traverseQueryExtra,
|
|
20
16
|
setExpandStatus,
|
|
21
17
|
getUnderlyingBuiltinType,
|
|
18
|
+
forEachGeneric,
|
|
19
|
+
forEachInOrder,
|
|
22
20
|
} = require('./utils');
|
|
23
21
|
const { Location } = require('../base/location');
|
|
24
22
|
const { CompilerAssertion } = require('../base/error');
|
package/lib/compiler/utils.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const { dictAdd,
|
|
13
|
+
const { dictAdd, dictFirst } = require('../base/dictionaries');
|
|
14
14
|
const { Location, weakLocation } = require('../base/location');
|
|
15
15
|
const { XsnName, XsnArtifact } = require('./xsn-model');
|
|
16
16
|
|
|
@@ -145,10 +145,11 @@ function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDepreca
|
|
|
145
145
|
* @return {XSN.Artifact}
|
|
146
146
|
*/
|
|
147
147
|
function initItemsLinks( obj, block ) {
|
|
148
|
+
const parent = obj._parent;
|
|
148
149
|
let { items } = obj;
|
|
149
150
|
while (items) {
|
|
150
151
|
setLink( items, '_outer', obj );
|
|
151
|
-
setLink( items, '_parent',
|
|
152
|
+
setLink( items, '_parent', parent );
|
|
152
153
|
setLink( items, '_block', block );
|
|
153
154
|
obj = items;
|
|
154
155
|
items = obj.items;
|
|
@@ -162,7 +163,7 @@ function initItemsLinks( obj, block ) {
|
|
|
162
163
|
* is most often the property `elem.name.id`.
|
|
163
164
|
*
|
|
164
165
|
* If argument `prop` is provided, add `elem` to the dictionary of that name,
|
|
165
|
-
* e.g. `elements`.
|
|
166
|
+
* e.g. `elements`. TODO: remove `prop`.
|
|
166
167
|
*/
|
|
167
168
|
function setMemberParent( elem, name, parent, prop ) {
|
|
168
169
|
if (prop) { // extension or structure include
|
|
@@ -286,21 +287,6 @@ function dependsOnSilent( user, art ) {
|
|
|
286
287
|
user._deps.push( { art } );
|
|
287
288
|
}
|
|
288
289
|
|
|
289
|
-
function storeExtension( elem, name, prop, parent ) {
|
|
290
|
-
if (prop === 'enum')
|
|
291
|
-
prop = 'elements';
|
|
292
|
-
const kind = `_${ elem.kind }`; // _extend or _annotate
|
|
293
|
-
if (!parent[kind])
|
|
294
|
-
setLink( parent, kind, {} );
|
|
295
|
-
// if (name === '' && prop === 'params') {
|
|
296
|
-
// pushToDict( parent[kind], 'returns', elem ); // not really a dict
|
|
297
|
-
// return;
|
|
298
|
-
// }
|
|
299
|
-
if (!parent[kind][prop])
|
|
300
|
-
parent[kind][prop] = Object.create( null );
|
|
301
|
-
pushToDict( parent[kind][prop], name, elem );
|
|
302
|
-
}
|
|
303
|
-
|
|
304
290
|
/** @type {(a: any, b: any) => boolean} */
|
|
305
291
|
const testFunctionPlaceholder = () => true;
|
|
306
292
|
|
|
@@ -348,11 +334,11 @@ function augmentPath( location, ...args ) {
|
|
|
348
334
|
return { path: args.map( id => ({ id, location }) ), location };
|
|
349
335
|
}
|
|
350
336
|
|
|
351
|
-
function copyExpr( expr, location ) {
|
|
337
|
+
function copyExpr( expr, location, noLinks = false ) {
|
|
352
338
|
if (!expr || typeof expr !== 'object')
|
|
353
339
|
return expr;
|
|
354
340
|
else if (Array.isArray( expr ))
|
|
355
|
-
return expr.map( e => copyExpr( e, location ) );
|
|
341
|
+
return expr.map( e => copyExpr( e, location, noLinks ) );
|
|
356
342
|
|
|
357
343
|
const proto = Object.getPrototypeOf( expr );
|
|
358
344
|
if (proto && proto !== Object.prototype && proto !== XsnName.prototype &&
|
|
@@ -363,16 +349,21 @@ function copyExpr( expr, location ) {
|
|
|
363
349
|
for (const prop of Object.getOwnPropertyNames( expr )) {
|
|
364
350
|
const pd = Object.getOwnPropertyDescriptor( expr, prop );
|
|
365
351
|
if (!proto)
|
|
366
|
-
r[prop] = copyExpr( pd.value, location );
|
|
367
|
-
|
|
368
|
-
else if (!pd.enumerable || prop.charAt(0) === '$')
|
|
369
|
-
Object.defineProperty( r, prop, pd );
|
|
352
|
+
r[prop] = copyExpr( pd.value, location, noLinks );
|
|
370
353
|
|
|
371
354
|
else if (prop === 'location')
|
|
372
355
|
r[prop] = location || pd.value;
|
|
373
356
|
|
|
374
|
-
else
|
|
375
|
-
r
|
|
357
|
+
else if (prop.charAt(0) === '$')
|
|
358
|
+
Object.defineProperty( r, prop, pd );
|
|
359
|
+
|
|
360
|
+
else if (pd.enumerable)
|
|
361
|
+
r[prop] = copyExpr( pd.value, location, noLinks );
|
|
362
|
+
|
|
363
|
+
else if (!noLinks)
|
|
364
|
+
Object.defineProperty( r, prop, pd );
|
|
365
|
+
// TODO: probably consider noLinks just for element references (_artifact of
|
|
366
|
+
// first path item has _main) - test calc element with `cast`
|
|
376
367
|
}
|
|
377
368
|
return r;
|
|
378
369
|
}
|
|
@@ -660,22 +651,37 @@ function isDirectComposition( art ) {
|
|
|
660
651
|
return path?.length === 1 && path[0].id === 'cds.Composition';
|
|
661
652
|
}
|
|
662
653
|
|
|
663
|
-
function targetCantBeAspect( elem, calledForTargetAspectProp ) {
|
|
654
|
+
function targetCantBeAspect( elem, calledForTargetAspectProp, definitions ) { // see #11658
|
|
664
655
|
// Remark: we do not check `on` and `keys` here - the error should complain
|
|
665
656
|
// at the `on`/`keys`, not the aspect
|
|
666
657
|
if (!isDirectComposition( elem ) || elem.targetAspect && !calledForTargetAspectProp)
|
|
667
658
|
return (elem.type && !elem.type.$inferred) ? 'std' : 'redirected';
|
|
659
|
+
// TODO: better msg if due to calledForTargetAspectProp
|
|
668
660
|
if (!elem._main)
|
|
669
661
|
return elem.kind; // type or annotation
|
|
670
662
|
// TODO: extra for "in many"?
|
|
671
663
|
let art = elem;
|
|
672
664
|
while (art.kind === 'element')
|
|
673
665
|
art = art._parent;
|
|
666
|
+
// Remark: if we include entity/aspect, we might still have a targetAspects in a
|
|
667
|
+
// type/event - see Error[type-managed-composition]
|
|
668
|
+
// With a future feature “managed compositions in sub elements“, we might allow
|
|
669
|
+
// moving an aspect to targetAspect independently from the main artifact
|
|
670
|
+
if (definitions && art.kind === 'extend')
|
|
671
|
+
art = definitions[art.name.id];
|
|
674
672
|
if (![ 'entity', 'aspect', 'event' ].includes( art.kind ))
|
|
675
673
|
return (art.kind !== 'mixin') ? art.kind : 'select';
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
674
|
+
if ((art.query || art.kind === 'event') && !(calledForTargetAspectProp && elem.target))
|
|
675
|
+
return art.kind;
|
|
676
|
+
if (elem._parent.kind === 'element')
|
|
677
|
+
return 'sub';
|
|
678
|
+
if (!calledForTargetAspectProp) {
|
|
679
|
+
if (elem.on)
|
|
680
|
+
return 'on';
|
|
681
|
+
if (elem.foreignKeys)
|
|
682
|
+
return 'keys';
|
|
683
|
+
}
|
|
684
|
+
return null;
|
|
679
685
|
}
|
|
680
686
|
|
|
681
687
|
function userQuery( user ) {
|
|
@@ -781,6 +787,50 @@ function compositionTextVariant( art, composition, association = 'std' ) {
|
|
|
781
787
|
: association;
|
|
782
788
|
}
|
|
783
789
|
|
|
790
|
+
// Looping: ---------------------------------------------------------------------
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Apply function `callback` to all artifacts in dictionary
|
|
794
|
+
* `model.definitions`. See function `forEachGeneric` for details.
|
|
795
|
+
* TODO: should we skip "namespaces" already here?
|
|
796
|
+
*/
|
|
797
|
+
function forEachDefinition( model, callback ) {
|
|
798
|
+
forEachGeneric( model, 'definitions', callback );
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Apply function `callback` to all members of object `construct` (main artifact
|
|
803
|
+
* or parent member). Members are considered those in dictionaries `elements`,
|
|
804
|
+
* `enum`, `actions` and `params` of `obj`.
|
|
805
|
+
*
|
|
806
|
+
* @param {XSN.Artifact} construct
|
|
807
|
+
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
808
|
+
* @param {XSN.Artifact} [target]
|
|
809
|
+
*/
|
|
810
|
+
function forEachMember( construct, callback ) {
|
|
811
|
+
forEachGeneric( construct, 'elements', callback );
|
|
812
|
+
forEachGeneric( construct, 'enum', callback );
|
|
813
|
+
forEachGeneric( construct, 'foreignKeys', callback );
|
|
814
|
+
forEachGeneric( construct, 'actions', callback );
|
|
815
|
+
forEachGeneric( construct, 'params', callback );
|
|
816
|
+
if (construct.returns)
|
|
817
|
+
callback( construct.returns, '', 'params' );
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Apply function `callback` to all objects in dictionary `parent[prop]`,
|
|
821
|
+
// including all duplicates (found under the same name). Function `callback` is
|
|
822
|
+
// called with the following arguments: the object, the name and `prop`.
|
|
823
|
+
function forEachGeneric( parent, prop, callback ) {
|
|
824
|
+
const dict = parent[prop];
|
|
825
|
+
for (const name in dict) {
|
|
826
|
+
const obj = dict[name];
|
|
827
|
+
const { $duplicates } = obj;
|
|
828
|
+
callback( obj, name, prop );
|
|
829
|
+
if (Array.isArray( $duplicates )) // redefinitions
|
|
830
|
+
$duplicates.forEach( o => callback( o, name, prop ) );
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
784
834
|
module.exports = {
|
|
785
835
|
pushLink,
|
|
786
836
|
annotationVal,
|
|
@@ -801,7 +851,6 @@ module.exports = {
|
|
|
801
851
|
initDollarSelf,
|
|
802
852
|
initDollarParameters,
|
|
803
853
|
initBoundSelfParam,
|
|
804
|
-
storeExtension,
|
|
805
854
|
withAssociation,
|
|
806
855
|
pathName,
|
|
807
856
|
augmentPath,
|
|
@@ -827,4 +876,8 @@ module.exports = {
|
|
|
827
876
|
definedViaCdl,
|
|
828
877
|
artifactRefLocation,
|
|
829
878
|
compositionTextVariant,
|
|
879
|
+
forEachDefinition,
|
|
880
|
+
forEachMember,
|
|
881
|
+
forEachGeneric,
|
|
882
|
+
forEachInOrder: forEachGeneric,
|
|
830
883
|
};
|
|
@@ -49,7 +49,7 @@ function addToSetAttr( carrier, propName, propValue, removeFromType = true ) {
|
|
|
49
49
|
|
|
50
50
|
function applyAppSpecificLateCsnTransformationOnElement( options, element, struct, error ) {
|
|
51
51
|
if (options.isV2() && struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
52
|
-
mapAnnotationAssignment(element, struct,
|
|
52
|
+
mapAnnotationAssignment(element, struct, GenerateAnalyticalAnnotations());
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
// etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
|
|
@@ -66,7 +66,7 @@ function applyAppSpecificLateCsnTransformationOnElement( options, element, struc
|
|
|
66
66
|
(struct['@Core.OptimisticConcurrency'] || [])/* .push(element.name) */);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function
|
|
69
|
+
function GenerateAnalyticalAnnotations() {
|
|
70
70
|
function mapCommonAttributes( elt, structure, prop ) {
|
|
71
71
|
const CommonAttributes = elt[prop];
|
|
72
72
|
if (!Array.isArray(CommonAttributes)) {
|