@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/model/csnUtils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp } = require('../base/model');
|
|
4
4
|
const { csnRefs } = require('../model/csnRefs');
|
|
5
|
+
const { isBuiltinType } = require('../compiler/builtins.js')
|
|
5
6
|
const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
|
|
6
7
|
const version = require('../../package.json').version;
|
|
7
8
|
|
|
@@ -11,8 +12,8 @@ const version = require('../../package.json').version;
|
|
|
11
12
|
* Generic Callback
|
|
12
13
|
*
|
|
13
14
|
* @callback genericCallback
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
15
|
+
* @param {any} art
|
|
16
|
+
* @param {string} name Artifact Name
|
|
16
17
|
* @param {string} prop Dictionary Property
|
|
17
18
|
* @param {CSN.Path} path Location
|
|
18
19
|
* @param {CSN.Artifact} [dictionary]
|
|
@@ -36,7 +37,7 @@ const version = require('../../package.json').version;
|
|
|
36
37
|
* @param {CSN.Model} model (Compact) CSN model
|
|
37
38
|
*/
|
|
38
39
|
function getUtils(model) {
|
|
39
|
-
const { artifactRef, inspectRef, effectiveType } = csnRefs(model);
|
|
40
|
+
const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
|
|
40
41
|
|
|
41
42
|
return {
|
|
42
43
|
getCsnDef,
|
|
@@ -58,8 +59,137 @@ function getUtils(model) {
|
|
|
58
59
|
inspectRef,
|
|
59
60
|
artifactRef,
|
|
60
61
|
effectiveType,
|
|
62
|
+
get$combined,
|
|
63
|
+
getOrigin,
|
|
61
64
|
};
|
|
62
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Compute and return $combined for the given query.
|
|
68
|
+
*
|
|
69
|
+
* @param {CSN.Query} query
|
|
70
|
+
* @returns {object}
|
|
71
|
+
*/
|
|
72
|
+
function get$combined(query) {
|
|
73
|
+
const sources = getSources(query);
|
|
74
|
+
return sources;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the union of all elements from the from clause
|
|
78
|
+
* - descend into unions, following the lead query
|
|
79
|
+
* - merge all queries in case of joins
|
|
80
|
+
* - follow subqueries
|
|
81
|
+
*
|
|
82
|
+
* @param {CSN.Query} query Query to check
|
|
83
|
+
* @returns {object} Map of sources
|
|
84
|
+
*/
|
|
85
|
+
function getSources(query, isSubquery=false) {
|
|
86
|
+
// Remark CW: better just a while along query.SET.args[0]
|
|
87
|
+
if (query.SET) {
|
|
88
|
+
if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
|
|
89
|
+
return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
|
|
90
|
+
|
|
91
|
+
return getSources(query.SET.args[0], isSubquery);
|
|
92
|
+
}
|
|
93
|
+
else if (query.SELECT) {
|
|
94
|
+
if (query.SELECT.from.args) {
|
|
95
|
+
return walkArgs(query.SELECT.from.args);
|
|
96
|
+
}
|
|
97
|
+
else if (query.SELECT.from.ref) {
|
|
98
|
+
let art = artifactRef(query.SELECT.from);
|
|
99
|
+
|
|
100
|
+
if(art.target)
|
|
101
|
+
art = artifactRef(art.target);
|
|
102
|
+
|
|
103
|
+
if(isSubquery && !query.SELECT.elements)
|
|
104
|
+
throw new Error('Expected subquery to have .elements');
|
|
105
|
+
|
|
106
|
+
return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
|
|
107
|
+
query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
|
|
108
|
+
query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
|
|
109
|
+
}
|
|
110
|
+
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
111
|
+
return getSources(query.SELECT.from, true);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function walkArgs(args) {
|
|
116
|
+
let elements = Object.create(null);
|
|
117
|
+
for (const arg of args) {
|
|
118
|
+
if (arg.args) {
|
|
119
|
+
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
120
|
+
}
|
|
121
|
+
else if (arg.ref) {
|
|
122
|
+
const art = artifactRef(arg);
|
|
123
|
+
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
124
|
+
}
|
|
125
|
+
else if (arg.SELECT || arg.SET) {
|
|
126
|
+
elements = mergeElementMaps(elements, getSources(arg));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return elements;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Merge two maps of elements together
|
|
137
|
+
*
|
|
138
|
+
* @param {object} mapA Map a - will be returned
|
|
139
|
+
* @param {object} mapB Map b - will not be returned
|
|
140
|
+
* @returns {object} mapA
|
|
141
|
+
*/
|
|
142
|
+
function mergeElementMaps(mapA, mapB) {
|
|
143
|
+
for (const elementName in mapB) {
|
|
144
|
+
if (!mapA[elementName])
|
|
145
|
+
mapA[elementName] = [];
|
|
146
|
+
|
|
147
|
+
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return mapA;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Merge elements into an existing map
|
|
155
|
+
*
|
|
156
|
+
* @param {any} existingMap map to merge into - will be returned
|
|
157
|
+
* @param {object} elements elements to merge into the map
|
|
158
|
+
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
159
|
+
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
160
|
+
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
161
|
+
* @returns {object} existingMap
|
|
162
|
+
*/
|
|
163
|
+
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
164
|
+
for (const elementName in elements) {
|
|
165
|
+
const element = elements[elementName];
|
|
166
|
+
if (!existingMap[elementName])
|
|
167
|
+
existingMap[elementName] = [];
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
existingMap[elementName].push({
|
|
171
|
+
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return existingMap;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Return the name part of the artifact name - no namespace etc.
|
|
181
|
+
* @param {string|object} name Absolute name of the artifact
|
|
182
|
+
*/
|
|
183
|
+
function getBaseName(name) {
|
|
184
|
+
if (!name)
|
|
185
|
+
return name;
|
|
186
|
+
|
|
187
|
+
if (name.id)
|
|
188
|
+
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
189
|
+
|
|
190
|
+
return name.substring( name.lastIndexOf('.')+1 )
|
|
191
|
+
}
|
|
192
|
+
}
|
|
63
193
|
|
|
64
194
|
|
|
65
195
|
/**
|
|
@@ -329,44 +459,53 @@ function getUtils(model) {
|
|
|
329
459
|
}
|
|
330
460
|
}
|
|
331
461
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Resolve to the final type of a type, that means follow type chains, references to other types or
|
|
465
|
+
* elements a.s.o
|
|
466
|
+
* Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
|
|
467
|
+
* Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
|
|
468
|
+
* else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
|
|
469
|
+
* e.g. given the following model
|
|
470
|
+
* type bar: S.foo;
|
|
471
|
+
* type s1 {
|
|
472
|
+
* s: s2;
|
|
473
|
+
* };
|
|
474
|
+
* type s2 {
|
|
475
|
+
* u: type of S.e:t;
|
|
476
|
+
* }
|
|
477
|
+
* service S {
|
|
478
|
+
* type foo: type of S.e:i.j1;
|
|
479
|
+
* entity e {
|
|
480
|
+
* key i: { j1: Integer };
|
|
481
|
+
* t: bar;
|
|
482
|
+
* v: s1;
|
|
483
|
+
* x: blutz.s.u;
|
|
484
|
+
* };
|
|
485
|
+
* type blutz: S.e.v;
|
|
486
|
+
* view V as select from e {
|
|
487
|
+
* 1+1 as i: bar,
|
|
488
|
+
* };
|
|
489
|
+
* type tt: type of V:i;
|
|
490
|
+
* }
|
|
491
|
+
* the following calls will all return 'cds.Integer'
|
|
492
|
+
* getFinalBaseType('S.tt')
|
|
493
|
+
* getFinalBaseType('S.e',['i','j1'])
|
|
494
|
+
* getFinalBaseType('S.e',['t'])
|
|
495
|
+
* getFinalBaseType('S.e',['x'])
|
|
496
|
+
* getFinalBaseType('S.blutz',['s', 'u'])
|
|
497
|
+
* Types are always resolved as far as possible. A type name which has no further definition is simply returned.
|
|
498
|
+
* Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
|
|
499
|
+
* the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
|
|
500
|
+
* no type (e.g. expr in a view without explicit type) returns 'null'
|
|
501
|
+
*
|
|
502
|
+
* @param {string|object} type Type - either string or ref
|
|
503
|
+
* @param {CSN.Path} path
|
|
504
|
+
* @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
|
|
505
|
+
* @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
|
|
506
|
+
* @returns
|
|
507
|
+
*/
|
|
508
|
+
function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
|
|
370
509
|
if (!type)
|
|
371
510
|
return type;
|
|
372
511
|
if (typeof(type) === 'string') {
|
|
@@ -384,24 +523,24 @@ function getUtils(model) {
|
|
|
384
523
|
}
|
|
385
524
|
let definedType = model.definitions[type];
|
|
386
525
|
if (definedType && definedType.type)
|
|
387
|
-
return getFinalBaseType(definedType.type, path, cycleCheck);
|
|
526
|
+
return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
|
|
388
527
|
else
|
|
389
|
-
return getFinalBaseType(definedType, path, cycleCheck);
|
|
528
|
+
return getFinalBaseType(definedType, path, resolved, cycleCheck);
|
|
390
529
|
}
|
|
391
530
|
else if (typeof(type) === 'object') {
|
|
392
531
|
if (type.ref) {
|
|
393
532
|
// assert type.ref instanceof Array && type.ref.length >= 1
|
|
394
|
-
const ref = artifactRef(type);
|
|
395
|
-
return getFinalBaseType(ref, path, cycleCheck);
|
|
533
|
+
const ref = resolved.has(type) ? resolved.get(type).art : artifactRef(type);
|
|
534
|
+
return getFinalBaseType(ref, path, resolved, cycleCheck);
|
|
396
535
|
}
|
|
397
536
|
else if (type.elements) {
|
|
398
537
|
if (path.length) {
|
|
399
538
|
let [e, ...p] = path;
|
|
400
|
-
return getFinalBaseType(type.elements[e], p, cycleCheck);
|
|
539
|
+
return getFinalBaseType(type.elements[e], p, resolved, cycleCheck);
|
|
401
540
|
}
|
|
402
541
|
}
|
|
403
542
|
else if (type.type)
|
|
404
|
-
return (getFinalBaseType(type.type, path, cycleCheck));
|
|
543
|
+
return (getFinalBaseType(type.type, path, resolved, cycleCheck));
|
|
405
544
|
else if (type.items)
|
|
406
545
|
return type;
|
|
407
546
|
else
|
|
@@ -414,16 +553,6 @@ function getUtils(model) {
|
|
|
414
553
|
}
|
|
415
554
|
}
|
|
416
555
|
|
|
417
|
-
// Tell if a type is (directly) a builtin type
|
|
418
|
-
// Note that in CSN builtins are not in the definition of the model, so we can only check against their absolute names.
|
|
419
|
-
// Builtin types are "cds.<something>", i.e. they are directly in 'cds', but not for example
|
|
420
|
-
// in 'cds.foundation'. Also note, that a type might be a ref object, that refers to something else,
|
|
421
|
-
// so if you consider type chains don't forget first to resolve to the final type before
|
|
422
|
-
function isBuiltinType(type) {
|
|
423
|
-
return typeof(type) === 'string' && type.startsWith('cds.') && !type.startsWith('cds.foundation.')
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
556
|
/**
|
|
428
557
|
* Deeply clone the given CSN model and return it.
|
|
429
558
|
* In testMode (or with testSortCsn), definitions are sorted.
|
|
@@ -493,12 +622,12 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
493
622
|
// Backends rely on the fact that `forEachElement` also goes through all
|
|
494
623
|
// `elements` of the return type (if structured).
|
|
495
624
|
// TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
|
|
496
|
-
if (construct.returns) {
|
|
625
|
+
if (construct.returns && !iterateOptions.elementsOnly) {
|
|
497
626
|
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
|
|
498
627
|
}
|
|
499
628
|
|
|
500
629
|
path = [...path]; // Copy
|
|
501
|
-
const propsWithMembers = ['elements', 'enum', 'foreignKeys', 'actions', 'params'];
|
|
630
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
|
|
502
631
|
propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
|
|
503
632
|
}
|
|
504
633
|
|
|
@@ -679,11 +808,25 @@ function forAllQueries(query, callback, path = []){
|
|
|
679
808
|
}
|
|
680
809
|
}
|
|
681
810
|
|
|
682
|
-
function forAllElements(artifact, artifactName, cb){
|
|
811
|
+
function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
683
812
|
if(artifact.elements) {
|
|
684
813
|
cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
|
|
685
814
|
}
|
|
686
815
|
|
|
816
|
+
if(includeActions && artifact.actions) {
|
|
817
|
+
Object.entries(artifact.actions).forEach( ([actionName, action]) => {
|
|
818
|
+
const path = ['definitions', artifactName, 'actions', actionName];
|
|
819
|
+
if(action.params) {
|
|
820
|
+
Object.entries(action.params).forEach( ([paramName, param]) => {
|
|
821
|
+
if(param.elements)
|
|
822
|
+
cb(param, param.elements, path.concat(['params', paramName, 'elements']));
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
if(action.returns && action.returns.elements)
|
|
826
|
+
cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
687
830
|
if(artifact.query) {
|
|
688
831
|
forAllQueries(artifact.query, (q, p) => {
|
|
689
832
|
const s = q.SELECT;
|
|
@@ -733,7 +876,7 @@ function hasAnnotationValue(artifact, annotationName, expected = true) {
|
|
|
733
876
|
* function accepts EDM internal and external options
|
|
734
877
|
*
|
|
735
878
|
* @param {CSN.Element} elementCsn
|
|
736
|
-
* @param {
|
|
879
|
+
* @param {ODataOptions} options EDM specific options
|
|
737
880
|
*/
|
|
738
881
|
function isEdmPropertyRendered(elementCsn, options) {
|
|
739
882
|
if(options.toOdata)
|
|
@@ -783,10 +926,10 @@ function getArtifactDatabaseNameOf(artifactName, namingConvention, csn) {
|
|
|
783
926
|
throw new Error('Unknown naming convention: ' + namingConvention);
|
|
784
927
|
}
|
|
785
928
|
else {
|
|
786
|
-
const namespace = csn;
|
|
787
929
|
console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
|
|
788
930
|
if (namingConvention === 'hdbcds') {
|
|
789
|
-
if (
|
|
931
|
+
if (csn) {
|
|
932
|
+
const namespace = String(csn);
|
|
790
933
|
return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
|
|
791
934
|
}
|
|
792
935
|
return artifactName;
|
|
@@ -922,11 +1065,12 @@ function getElementDatabaseNameOf(elemName, namingConvention) {
|
|
|
922
1065
|
*
|
|
923
1066
|
* @param {object} csn CSN to enrich in-place
|
|
924
1067
|
* @param {object} customTransformers Map of prop to transform and function to apply
|
|
925
|
-
* @param {Function[]} artifactTransformers Transformations to run on the artifacts, like forEachDefinition
|
|
926
|
-
* @param {Boolean} skipIgnore Wether to skip _ignore elements or not
|
|
1068
|
+
* @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
|
|
1069
|
+
* @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
|
|
1070
|
+
* @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
|
|
927
1071
|
* @returns {object} CSN with transformations applied
|
|
928
1072
|
*/
|
|
929
|
-
function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true ) {
|
|
1073
|
+
function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
|
|
930
1074
|
const transformers = {
|
|
931
1075
|
elements: dictionary,
|
|
932
1076
|
definitions: dictionary,
|
|
@@ -959,7 +1103,7 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
|
|
|
959
1103
|
for (let name of Object.getOwnPropertyNames( node )) {
|
|
960
1104
|
const trans = transformers[name] || standard;
|
|
961
1105
|
if(customTransformers[name])
|
|
962
|
-
customTransformers[name](node, name, node[name], csnPath);
|
|
1106
|
+
customTransformers[name](node, name, node[name], csnPath, parent, prop);
|
|
963
1107
|
|
|
964
1108
|
trans( node, name, node[name], csnPath );
|
|
965
1109
|
}
|
|
@@ -968,6 +1112,9 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
|
|
|
968
1112
|
}
|
|
969
1113
|
|
|
970
1114
|
function dictionary( node, prop, dict ) {
|
|
1115
|
+
// Allow skipping dicts like actions in forHanaNew
|
|
1116
|
+
if(options.skipDict && options.skipDict[prop])
|
|
1117
|
+
return;
|
|
971
1118
|
csnPath.push( prop );
|
|
972
1119
|
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
973
1120
|
standard( dict, name, dict[name] );
|
|
@@ -980,8 +1127,11 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
|
|
|
980
1127
|
function definitions( node, prop, dict ) {
|
|
981
1128
|
csnPath.push( prop );
|
|
982
1129
|
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
983
|
-
|
|
984
|
-
|
|
1130
|
+
const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
|
|
1131
|
+
if(!skip) {
|
|
1132
|
+
artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
|
|
1133
|
+
standard( dict, name, dict[name] );
|
|
1134
|
+
}
|
|
985
1135
|
}
|
|
986
1136
|
if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
|
|
987
1137
|
setProp(node, '$' + prop, dict);
|
|
@@ -994,10 +1144,14 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
|
|
|
994
1144
|
path.forEach( function step( s, i ) {
|
|
995
1145
|
if (s && typeof s === 'object') {
|
|
996
1146
|
csnPath.push( i );
|
|
997
|
-
if
|
|
998
|
-
standard(
|
|
999
|
-
|
|
1000
|
-
|
|
1147
|
+
if(options.drillRef) {
|
|
1148
|
+
standard(path, i, s);
|
|
1149
|
+
} else {
|
|
1150
|
+
if (s.args)
|
|
1151
|
+
standard( s, 'args', s.args );
|
|
1152
|
+
if (s.where)
|
|
1153
|
+
standard( s, 'where', s.where );
|
|
1154
|
+
}
|
|
1001
1155
|
csnPath.pop();
|
|
1002
1156
|
}
|
|
1003
1157
|
} );
|
|
@@ -1383,7 +1537,7 @@ function sortCsnDefinitionsForTests(csn, options) {
|
|
|
1383
1537
|
if (!options.testMode)
|
|
1384
1538
|
return;
|
|
1385
1539
|
const sorted = Object.create(null);
|
|
1386
|
-
Object.keys(csn.definitions).sort().forEach((name) => {
|
|
1540
|
+
Object.keys(csn.definitions || {}).sort().forEach((name) => {
|
|
1387
1541
|
sorted[name] = csn.definitions[name];
|
|
1388
1542
|
});
|
|
1389
1543
|
csn.definitions = sorted;
|
|
@@ -1393,7 +1547,7 @@ function sortCsnDefinitionsForTests(csn, options) {
|
|
|
1393
1547
|
* Return an array of non-abstract service names contained in CSN
|
|
1394
1548
|
*
|
|
1395
1549
|
* @param {CSN.Model} csn
|
|
1396
|
-
* @returns {
|
|
1550
|
+
* @returns {string[]}
|
|
1397
1551
|
*/
|
|
1398
1552
|
function getServiceNames(csn) {
|
|
1399
1553
|
let result = [];
|
|
@@ -1405,6 +1559,60 @@ function getServiceNames(csn) {
|
|
|
1405
1559
|
return result;
|
|
1406
1560
|
}
|
|
1407
1561
|
|
|
1562
|
+
/**
|
|
1563
|
+
* Check wether the artifact is @cds.persistence.skip
|
|
1564
|
+
*
|
|
1565
|
+
* @param {CSN.Artifact} artifact
|
|
1566
|
+
* @returns {Boolean}
|
|
1567
|
+
*/
|
|
1568
|
+
function isSkipped(artifact) {
|
|
1569
|
+
return hasAnnotationValue(artifact, '@cds.persistence.skip', true)
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* Walk path in the CSN and return the result.
|
|
1574
|
+
*
|
|
1575
|
+
* @param {CSN.Model} csn
|
|
1576
|
+
* @param {CSN.Path} path
|
|
1577
|
+
* @returns {object} Whatever is at the end of path
|
|
1578
|
+
*/
|
|
1579
|
+
function walkCsnPath(csn, path) {
|
|
1580
|
+
/** @type {object} */
|
|
1581
|
+
let obj = csn;
|
|
1582
|
+
for(let i = 0; i < path.length; i++){
|
|
1583
|
+
obj = obj[path[i]];
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
return obj;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* If provided, get the replacement string for the given magic variable ref.
|
|
1591
|
+
* No validation is done that the ref is actually magic!
|
|
1592
|
+
*
|
|
1593
|
+
* @param {array} ref
|
|
1594
|
+
* @param {CSN.Options} options
|
|
1595
|
+
* @returns {string|null}
|
|
1596
|
+
*/
|
|
1597
|
+
function getVariableReplacement(ref, options) {
|
|
1598
|
+
if(options && options.variableReplacements) {
|
|
1599
|
+
let replacement = options.variableReplacements;
|
|
1600
|
+
for(let i = 0; i < ref.length; i++) {
|
|
1601
|
+
replacement = replacement[ref[i]];
|
|
1602
|
+
if(replacement === undefined)
|
|
1603
|
+
return null;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
if(replacement === undefined)
|
|
1607
|
+
return null; // no valid replacement found
|
|
1608
|
+
else if(typeof replacement === 'string')
|
|
1609
|
+
return replacement; // valid replacement
|
|
1610
|
+
else
|
|
1611
|
+
return null; // $user.foo, but we only have configured $user.foo.bar -> error
|
|
1612
|
+
} else {
|
|
1613
|
+
return null;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1408
1616
|
|
|
1409
1617
|
module.exports = {
|
|
1410
1618
|
getUtils,
|
|
@@ -1444,4 +1652,7 @@ module.exports = {
|
|
|
1444
1652
|
getNamespace,
|
|
1445
1653
|
sortCsnDefinitionsForTests,
|
|
1446
1654
|
getServiceNames,
|
|
1655
|
+
isSkipped,
|
|
1656
|
+
walkCsnPath,
|
|
1657
|
+
getVariableReplacement
|
|
1447
1658
|
};
|
package/lib/model/enrichCsn.js
CHANGED
|
@@ -39,16 +39,20 @@ function enrichCsn( csn, options = {} ) {
|
|
|
39
39
|
params: dictionary,
|
|
40
40
|
enum: dictionary,
|
|
41
41
|
mixin: dictionary,
|
|
42
|
+
returns: definition,
|
|
43
|
+
items: definition,
|
|
42
44
|
ref: pathRef,
|
|
43
45
|
type: simpleRef,
|
|
44
46
|
targetAspect: simpleRef,
|
|
45
47
|
target: simpleRef,
|
|
46
48
|
includes: simpleRef,
|
|
49
|
+
$origin,
|
|
47
50
|
// TODO: excluding
|
|
48
51
|
'@': () => { /* ignore annotations */ },
|
|
49
52
|
}
|
|
50
53
|
setLocations( csn, false, null );
|
|
51
|
-
const { inspectRef, artifactRef, __getCache_forEnrichCsnDebugging } =
|
|
54
|
+
const { inspectRef, artifactRef, getOrigin, __getCache_forEnrichCsnDebugging } =
|
|
55
|
+
csnRefs( csn );
|
|
52
56
|
let $$cacheObjectNumber = 0; // for debugging
|
|
53
57
|
const csnPath = [];
|
|
54
58
|
if (csn.definitions)
|
|
@@ -79,10 +83,19 @@ function enrichCsn( csn, options = {} ) {
|
|
|
79
83
|
csnPath.pop();
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
function definition( parent, prop, obj ) {
|
|
87
|
+
const origin = getOrigin( obj ); // before standard for implicit protos inside
|
|
88
|
+
standard( parent, prop, obj );
|
|
89
|
+
if (obj.$origin === undefined && origin != null)
|
|
90
|
+
obj._origin = refLocation( origin );
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
function dictionary( parent, prop, dict ) {
|
|
94
|
+
if (!dict) // value null for inheritance interruption
|
|
95
|
+
return;
|
|
83
96
|
csnPath.push( prop );
|
|
84
97
|
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
85
|
-
|
|
98
|
+
definition( dict, name, dict[name] );
|
|
86
99
|
}
|
|
87
100
|
if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
|
|
88
101
|
parent['$'+prop] = dict;
|
|
@@ -119,6 +132,25 @@ function enrichCsn( csn, options = {} ) {
|
|
|
119
132
|
// parent['_' + prop] = e.toString(); }
|
|
120
133
|
}
|
|
121
134
|
|
|
135
|
+
function $origin( parent, prop, ref ) {
|
|
136
|
+
if (options.testMode) {
|
|
137
|
+
if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
|
|
138
|
+
parent._origin = refLocation( getOrigin( parent, true ) );
|
|
139
|
+
else if ( ref )
|
|
140
|
+
standard( parent, prop, ref )
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
try {
|
|
144
|
+
if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
|
|
145
|
+
parent._origin = refLocation( getOrigin( parent, true ) );
|
|
146
|
+
else if ( ref )
|
|
147
|
+
standard( parent, prop, ref )
|
|
148
|
+
} catch (e) {
|
|
149
|
+
parent._origin = e.toString();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
122
154
|
function pathRef( parent, prop, path ) {
|
|
123
155
|
const { links, art, scope, $env } = (() => {
|
|
124
156
|
if (options.testMode)
|
|
@@ -154,10 +186,10 @@ function enrichCsn( csn, options = {} ) {
|
|
|
154
186
|
csnPath.pop();
|
|
155
187
|
}
|
|
156
188
|
|
|
157
|
-
function _cache_debug( obj ) {
|
|
189
|
+
function _cache_debug( obj, subCache ) {
|
|
158
190
|
if (options.enrichCsn !== 'DEBUG')
|
|
159
191
|
return;
|
|
160
|
-
const cache = __getCache_forEnrichCsnDebugging( obj );
|
|
192
|
+
const cache = subCache || __getCache_forEnrichCsnDebugging( obj );
|
|
161
193
|
if (!cache)
|
|
162
194
|
return;
|
|
163
195
|
if (cache.$$objectNumber > 0) {
|
|
@@ -180,8 +212,20 @@ function enrichCsn( csn, options = {} ) {
|
|
|
180
212
|
}
|
|
181
213
|
else if (name[0] !== '$' || !Object.getPrototypeOf( val )) {
|
|
182
214
|
// ‹name›: dictionary of CSN nodes,
|
|
183
|
-
// ‹$name›: dictionary of cache values if no prototype
|
|
184
|
-
|
|
215
|
+
// ‹$name›: dictionary of cache values if no prototype
|
|
216
|
+
if (name !== '$aliases') {
|
|
217
|
+
obj.$$cacheObject[name] = Object.keys( val ); // TODO: or dict?
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const sub = Object.create(null);
|
|
221
|
+
for (const n in val) {
|
|
222
|
+
const alias = val[n];
|
|
223
|
+
const c = {};
|
|
224
|
+
_cache_debug( c, alias );
|
|
225
|
+
sub[n] = c.$$cacheObject;
|
|
226
|
+
}
|
|
227
|
+
obj.$$cacheObject[name] = sub;
|
|
228
|
+
}
|
|
185
229
|
}
|
|
186
230
|
else if (Array.isArray( val )) {
|
|
187
231
|
obj.$$cacheObject[name] = val.map( item => {
|