@sap/cds-compiler 2.11.4 → 2.12.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 +58 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +9 -10
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +12 -0
- package/lib/api/main.js +2 -0
- package/lib/api/options.js +2 -2
- package/lib/base/message-registry.js +31 -2
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +97 -69
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/checks.js +32 -9
- package/lib/compiler/definer.js +25 -4
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolver.js +97 -6
- package/lib/compiler/shared.js +12 -1
- package/lib/compiler/utils.js +7 -0
- package/lib/edm/annotations/genericTranslation.js +34 -17
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +1 -1
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +30 -23
- package/lib/edm/edmUtils.js +11 -12
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +15 -14
- package/lib/gen/languageLexer.interp +9 -1
- package/lib/gen/languageLexer.js +830 -779
- package/lib/gen/languageLexer.tokens +7 -6
- package/lib/gen/languageParser.js +2401 -2282
- package/lib/json/from-csn.js +47 -16
- package/lib/json/to-csn.js +17 -5
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/genericAntlrParser.js +68 -51
- package/lib/language/language.g4 +128 -74
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +5 -3
- package/lib/main.js +3 -2
- package/lib/model/csnRefs.js +116 -68
- package/lib/model/csnUtils.js +40 -48
- package/lib/model/enrichCsn.js +30 -14
- package/lib/optionProcessor.js +3 -3
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +193 -79
- package/lib/render/toHdbcds.js +179 -95
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +57 -40
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +6 -4
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +4 -5
- package/lib/transform/db/flattening.js +5 -6
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +36 -23
- package/lib/transform/forHanaNew.js +35 -626
- package/lib/transform/forOdataNew.js +5 -4
- package/lib/transform/localized.js +3 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +13 -13
- package/lib/transform/translateAssocsToJoins.js +8 -8
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +2 -1
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
|
@@ -191,10 +191,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
191
191
|
generateForeignKeys(csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember);
|
|
192
192
|
|
|
193
193
|
// Apply default type facets as set by options
|
|
194
|
-
// Flatten on-conditions in unmanaged associations
|
|
194
|
+
// Flatten on-conditions in unmanaged associations
|
|
195
195
|
/* FIXME (HJB): Is this comment still correct? processOnCond only strips $self
|
|
196
196
|
We should not remove $self prefixes in structured OData to not
|
|
197
|
-
interfer with path resolution
|
|
197
|
+
interfer with path resolution
|
|
198
198
|
*/
|
|
199
199
|
// This must be done before all the draft logic as all
|
|
200
200
|
// composition targets are annotated with @odata.draft.enabled in this step
|
|
@@ -303,8 +303,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
303
303
|
// If @Core.Computed is explicitly set, don't overwrite it!
|
|
304
304
|
if (node['@Core.Computed'] !== undefined) return;
|
|
305
305
|
|
|
306
|
-
// For @odata.on.insert/update,
|
|
307
|
-
|
|
306
|
+
// For @odata.on.insert/update, also add @Core.Computed
|
|
307
|
+
// @odata.on is deprecated, use @cds.on {update|insert} instead
|
|
308
|
+
if(['@odata.on.insert', '@odata.on.update', '@cds.on.insert', '@cds.on.update'].some(a => node[a]))
|
|
308
309
|
node['@Core.Computed'] = true;
|
|
309
310
|
}
|
|
310
311
|
|
|
@@ -5,14 +5,12 @@ const { setProp } = require('../base/model');
|
|
|
5
5
|
const { hasErrors } = require('../base/messages');
|
|
6
6
|
const { cloneCsnDictionary } = require('../model/csnUtils');
|
|
7
7
|
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
8
|
-
const { rejectManagedAssociationsAndStructuresForHdbcsNames } = require('../checks/selectItems');
|
|
9
8
|
const {
|
|
10
9
|
cloneCsn,
|
|
11
10
|
forEachDefinition,
|
|
12
11
|
forEachGeneric,
|
|
13
12
|
forAllQueries,
|
|
14
13
|
sortCsnDefinitionsForTests,
|
|
15
|
-
getUtils,
|
|
16
14
|
} = require('../model/csnUtils');
|
|
17
15
|
|
|
18
16
|
/**
|
|
@@ -79,7 +77,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
79
77
|
if (hasExistingLocalizationViews(csn, options))
|
|
80
78
|
return csn;
|
|
81
79
|
|
|
82
|
-
const { info
|
|
80
|
+
const { info } = makeMessageFunction(csn, options);
|
|
83
81
|
|
|
84
82
|
const noCoalesce = (options.localizedLanguageFallback === 'none' ||
|
|
85
83
|
options.localizedWithoutCoalesce);
|
|
@@ -87,16 +85,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
87
85
|
createDirectConvenienceViews(); // 1
|
|
88
86
|
createTransitiveConvenienceViews(); // 2 + 3
|
|
89
87
|
|
|
90
|
-
forEachDefinition(csn, (definition,
|
|
91
|
-
cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor)
|
|
92
|
-
if(definition.query) {
|
|
93
|
-
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
94
|
-
const that = { csnUtils: getUtils(csn), options, error };
|
|
95
|
-
rejectManagedAssociationsAndStructuresForHdbcsNames.call(that, definition, path)
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
|
|
100
89
|
|
|
101
90
|
sortCsnDefinitionsForTests(csn, options);
|
|
102
91
|
return csn;
|
|
@@ -144,7 +133,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
144
133
|
|
|
145
134
|
art[_hasLocalizedView] = viewName;
|
|
146
135
|
|
|
147
|
-
if(acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
136
|
+
if (acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
148
137
|
return;
|
|
149
138
|
|
|
150
139
|
let view;
|
|
@@ -201,13 +201,13 @@ module.exports = function (csn, options, referenceFlattener, csnUtils, error, is
|
|
|
201
201
|
// Transfer selected type properties from target key element
|
|
202
202
|
// FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
|
|
203
203
|
for (let prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
|
|
204
|
-
if (fkArtifact[prop]
|
|
204
|
+
if (fkArtifact[prop] !== undefined) {
|
|
205
205
|
foreignKeyElement[prop] = fkArtifact[prop];
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
// If the association is non-fkArtifact resp. key, so should be the foreign key field
|
|
209
209
|
for (let prop of ['notNull', 'key']) {
|
|
210
|
-
if (assoc[prop]
|
|
210
|
+
if (assoc[prop] !== undefined) {
|
|
211
211
|
foreignKeyElement[prop] = assoc[prop];
|
|
212
212
|
}
|
|
213
213
|
}
|
|
@@ -9,7 +9,7 @@ const { setProp } = require('../base/model');
|
|
|
9
9
|
const { csnRefs } = require('../model/csnRefs');
|
|
10
10
|
|
|
11
11
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const { cloneCsn, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
12
|
+
const { cloneCsn, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
13
13
|
|
|
14
14
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
15
15
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
@@ -108,7 +108,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
108
108
|
// Transfer selected type properties from target key element
|
|
109
109
|
// FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
|
|
110
110
|
for (const prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
|
|
111
|
-
if (fkArtifact[prop]
|
|
111
|
+
if (fkArtifact[prop] !== undefined) {
|
|
112
112
|
foreignKeyElement[prop] = fkArtifact[prop];
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -118,7 +118,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
118
118
|
|
|
119
119
|
// If the association is non-fkArtifact resp. key, so should be the foreign key field
|
|
120
120
|
for (const prop of ['notNull', 'key']) {
|
|
121
|
-
if (assoc[prop]
|
|
121
|
+
if (assoc[prop] !== undefined) {
|
|
122
122
|
foreignKeyElement[prop] = assoc[prop];
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -302,7 +302,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
302
302
|
* [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
|
|
303
303
|
* [ (Entity), (struct1_struct2_assoc), (elem) ]
|
|
304
304
|
*
|
|
305
|
-
* @param {string[]} ref
|
|
305
|
+
* @param {string[]} ref
|
|
306
306
|
* @param {CSN.Path} path CSN path to the ref
|
|
307
307
|
* @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
|
|
308
308
|
* @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
|
|
@@ -351,7 +351,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
351
351
|
|
|
352
352
|
/**
|
|
353
353
|
* Copy properties of the referenced type, but don't resolve to the final base type.
|
|
354
|
-
*
|
|
354
|
+
*
|
|
355
355
|
* Do not copy the length if it was just set via the default-value.
|
|
356
356
|
*
|
|
357
357
|
* @param {any} node Node to copy to
|
|
@@ -387,7 +387,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
387
387
|
* also unravel derived enum types, i.e. take the final base type of the enum's base type.
|
|
388
388
|
* Similar with associations and compositions (we probably need a _baseType link)
|
|
389
389
|
*
|
|
390
|
-
* @param {CSN.Artifact} node
|
|
390
|
+
* @param {CSN.Artifact} node
|
|
391
391
|
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
392
392
|
* @param {boolean} [keepLocalized=false] Wether to clone .localized from a type def
|
|
393
393
|
* @returns {void}
|
|
@@ -406,7 +406,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
406
406
|
delete node.type; // delete the type reference as edm processing does not expect it
|
|
407
407
|
} else if(finalBaseType.items) {
|
|
408
408
|
// This changes the order - to be discussed!
|
|
409
|
-
node.items = cloneCsn(finalBaseType, options)
|
|
409
|
+
node.items = cloneCsn(finalBaseType.items, options); // copy items
|
|
410
410
|
delete node.type;
|
|
411
411
|
} else {
|
|
412
412
|
node.type=finalBaseType;
|
|
@@ -424,16 +424,16 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
424
424
|
return;
|
|
425
425
|
|
|
426
426
|
// cloneCsn only works correctly if we start "from the top"
|
|
427
|
-
const
|
|
427
|
+
const cloneTypeDef = cloneCsn(typeDef, options);
|
|
428
428
|
// With hdbcds-hdbcds, don't resolve structured types - but propagrate ".items", to turn into LargeString later on.
|
|
429
429
|
if(typeDef.items) {
|
|
430
430
|
delete node.type;
|
|
431
|
-
Object.assign(node, {items:
|
|
431
|
+
Object.assign(node, {items: cloneTypeDef.items});
|
|
432
432
|
}
|
|
433
433
|
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
434
434
|
if(!typeDef.items)
|
|
435
435
|
delete node.type;
|
|
436
|
-
Object.assign(node, {elements:
|
|
436
|
+
Object.assign(node, {elements: cloneTypeDef.elements});
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
|
|
@@ -441,7 +441,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
441
441
|
}
|
|
442
442
|
// if the declared element is an enum, these values are with priority
|
|
443
443
|
if (!node.enum && typeDef.enum) {
|
|
444
|
-
const clone =
|
|
444
|
+
const clone = cloneCsnDictionary(typeDef.enum, options);
|
|
445
445
|
Object.assign(node, { enum: clone });
|
|
446
446
|
}
|
|
447
447
|
if (node.length === undefined && typeDef.length !== undefined)
|
|
@@ -865,7 +865,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
865
865
|
function checkMultipleAssignments(array, annoName, artifact, artifactName, err = true) {
|
|
866
866
|
if (array.length > 1) {
|
|
867
867
|
const loc = ['definitions', artifactName];
|
|
868
|
-
if (err
|
|
868
|
+
if (err === true) {
|
|
869
869
|
error(null, loc, { anno: annoName }, `Annotation $(ANNO) must be assigned only once`);
|
|
870
870
|
} else {
|
|
871
871
|
warning(null, loc, { anno: annoName },`Annotation $(ANNO) must be assigned only once`);
|
|
@@ -1084,7 +1084,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1084
1084
|
* Expand structured expression arguments to flat reference paths.
|
|
1085
1085
|
* Structured elements are real sub element lists and managed associations.
|
|
1086
1086
|
* All unmanaged association definitions are rewritten if applicable (elements/mixins).
|
|
1087
|
-
* Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
|
|
1087
|
+
* Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
|
|
1088
1088
|
* .xpr in columns.
|
|
1089
1089
|
*
|
|
1090
1090
|
* @todo Check if can be skipped for abstract entity and or cds.persistence.skip ?
|
|
@@ -315,7 +315,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
315
315
|
}
|
|
316
316
|
// if the found QA is the mixin QA and if the path length is one,
|
|
317
317
|
// this indicates the publishing of a mixin assoc, don't rewrite the path
|
|
318
|
-
if(QA.mixin && tail.length
|
|
318
|
+
if(QA.mixin && tail.length === 1)
|
|
319
319
|
return;
|
|
320
320
|
let pos = tail.indexOf(ps);
|
|
321
321
|
// cut off ps if it's a join relevant association with postfix
|
|
@@ -670,7 +670,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
670
670
|
if(fwdAssoc)
|
|
671
671
|
{
|
|
672
672
|
//env.assocStack.includes(fwdAssoc) => recursion
|
|
673
|
-
if(env.assocStack.length
|
|
673
|
+
if(env.assocStack.length === 2) {
|
|
674
674
|
// reuse (ugly) error message from forHana
|
|
675
675
|
error(null, env.assocStack[0].location,
|
|
676
676
|
{ name: '$self', id: '$self' },
|
|
@@ -702,7 +702,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
702
702
|
// ON cond of the forward assoc with swapped src/tgt aliases
|
|
703
703
|
let fwdAssoc = getForwardAssociationExpr(expr);
|
|
704
704
|
if(fwdAssoc) {
|
|
705
|
-
if(env.assocStack.length
|
|
705
|
+
if(env.assocStack.length === 2) {
|
|
706
706
|
// reuse (ugly) error message from forHana
|
|
707
707
|
error(null, expr.location, 'An association that uses “$self” in its ON-condition can\'t be compared to “$self”');
|
|
708
708
|
// don't check these paths again
|
|
@@ -736,7 +736,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
736
736
|
let newSrcAlias = tgtAlias;
|
|
737
737
|
let newTgtAlias = {};
|
|
738
738
|
// first try to identify table alias for complex views or redirected associations
|
|
739
|
-
if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
|
|
739
|
+
if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
|
|
740
740
|
// redirected target must have a $QA
|
|
741
741
|
fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA &&
|
|
742
742
|
// $QA's artifact must either be same srcAlias artifact
|
|
@@ -912,7 +912,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
912
912
|
combined list of elements made available by the from clause.
|
|
913
913
|
*/
|
|
914
914
|
let _navigation = undefined; // don't modify original path
|
|
915
|
-
if(env.assocStack.length
|
|
915
|
+
if(env.assocStack.length === 2) {
|
|
916
916
|
// a mixin assoc cannot have a structure prefix, it's sufficient to check head
|
|
917
917
|
if(head.id === env.assocStack.id()) {
|
|
918
918
|
// source side from view point of view (target side from forward point of view)
|
|
@@ -974,7 +974,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
974
974
|
|
|
975
975
|
// Return the original association if expr is a backlink term, undefined otherwise
|
|
976
976
|
function getForwardAssociationExpr(expr) {
|
|
977
|
-
if(expr.op && expr.op.val === '=' && expr.args.length
|
|
977
|
+
if(expr.op && expr.op.val === '=' && expr.args.length === 2) {
|
|
978
978
|
return getForwardAssociation(expr.args[0].path, expr.args[1].path);
|
|
979
979
|
}
|
|
980
980
|
return undefined;
|
|
@@ -983,10 +983,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
983
983
|
function getForwardAssociation(lhs, rhs) {
|
|
984
984
|
// [alpha.]BACKLINK.[beta.]FORWARD
|
|
985
985
|
if(lhs && rhs) {
|
|
986
|
-
if(rhs.length
|
|
986
|
+
if(rhs.length === 1 && rhs[0].id === '$self' &&
|
|
987
987
|
lhs.length > 1 && hasPrefix(lhs))
|
|
988
988
|
return lhs[lhs.length-1]._artifact;
|
|
989
|
-
if(lhs.length
|
|
989
|
+
if(lhs.length === 1 && lhs[0].id === '$self' &&
|
|
990
990
|
rhs.length > 1 && hasPrefix(rhs))
|
|
991
991
|
return rhs[rhs.length-1]._artifact;
|
|
992
992
|
}
|
|
@@ -1,67 +1,237 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { setProp } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
forEachDefinition,
|
|
6
|
+
forAllQueries,
|
|
7
7
|
getUtils,
|
|
8
|
-
|
|
8
|
+
forEachMember,
|
|
9
|
+
forEachMemberRecursively,
|
|
9
10
|
} = require('../model/csnUtils');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Loop through a universal CSN and enrich it with the properties
|
|
13
|
+
* Loop through a universal CSN and enrich it with the properties/annotations
|
|
13
14
|
* from the source definition - modifies the input model in-place
|
|
14
|
-
*
|
|
15
|
+
*
|
|
15
16
|
* @param {CSN.Model} csn
|
|
16
17
|
* @param {CSN.Options} options
|
|
17
18
|
*/
|
|
18
|
-
module.exports = function(csn, options) {
|
|
19
|
-
let { getOrigin,
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
module.exports = function (csn, options) {
|
|
20
|
+
let { getOrigin, getQueryPrimarySource, artifactRef } = getUtils(csn);
|
|
21
|
+
|
|
22
|
+
// Properties on definition level that we treat specially.
|
|
23
|
+
// TODO: There might be more annotations that will need special treatment
|
|
24
|
+
// see lib/compiler/propagator.js for reference
|
|
25
|
+
const defProps = {
|
|
26
|
+
'@cds.autoexpose': onlyViaArtifact,
|
|
27
|
+
'@fiori.draft.enabled': onlyViaArtifact,
|
|
28
|
+
'@': (prop, target, source) => { target[prop] = source[prop] },
|
|
29
|
+
// Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
|
|
30
|
+
'elements': onlyTypeDef,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// In this first loop through the model, missing properties in universal CSN
|
|
34
|
+
// are propagated so the CSN can become client one
|
|
35
|
+
forEachDefinition(csn, propagate);
|
|
36
|
+
|
|
37
|
+
// The $origin properties need to be removed separately
|
|
38
|
+
// as the values are used in csnRef::getOrigin that is used during
|
|
39
|
+
// the propagation above.
|
|
40
|
+
// Currently testMode-only for comparison against client CSN.
|
|
41
|
+
if (options.testMode) removeDollarProperties(csn);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Identify the sources of the passed object and propagate the relevant
|
|
45
|
+
* properties/annotations.
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} art Target object for propagation
|
|
48
|
+
*/
|
|
49
|
+
function propagate(art) {
|
|
50
|
+
// check if art was already processed by the status flag
|
|
51
|
+
// TODO: clean up later on, together with validator clean up probably or
|
|
52
|
+
// when this module is meant to be used standalone -> use internal cache to store already processed definitions?
|
|
53
|
+
if (art._status === 'propagated') return;
|
|
54
|
+
|
|
55
|
+
// collect chain of origins and propagate
|
|
56
|
+
// from the farthest to the nearest one to the target
|
|
57
|
+
let chain = [];
|
|
58
|
+
let target = art;
|
|
59
|
+
let origin = undefined;
|
|
60
|
+
do {
|
|
61
|
+
origin = getOrigin(target);
|
|
62
|
+
if (origin) {
|
|
63
|
+
chain.push({ target, origin });
|
|
64
|
+
target = origin;
|
|
65
|
+
}
|
|
66
|
+
} while (origin);
|
|
67
|
+
|
|
68
|
+
if (chain.length)
|
|
69
|
+
chain.reverse().forEach(propagateSingleStep);
|
|
70
|
+
else
|
|
71
|
+
// even if there weren't any found origin(s) on definition level
|
|
72
|
+
// we need to loop through the members
|
|
73
|
+
// TODO: construct use-/test-case where there might be the need that an origin chain
|
|
74
|
+
// needs to be constructed for members as well, specifically for this 'else'
|
|
75
|
+
// case where we do not run through the definitions
|
|
76
|
+
propagateMembersProps(target);
|
|
77
|
+
|
|
78
|
+
function propagateSingleStep({ target, origin }) {
|
|
79
|
+
// if target was already processed -> continue
|
|
80
|
+
if (target._status === 'propagated') return;
|
|
81
|
+
// propagate relevant definition level properties
|
|
82
|
+
// we check for kind as in the future the function should be
|
|
83
|
+
// generic and work for parts of CSN
|
|
84
|
+
if (target.kind) propagateDefProps(target, origin);
|
|
85
|
+
// propagate properties to members
|
|
86
|
+
propagateMembersProps(target);
|
|
87
|
+
|
|
88
|
+
setProp(target, '_status', 'propagated');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Propagate from 'source' to 'target' the relevant properties
|
|
93
|
+
* for CSN definitions.
|
|
94
|
+
*
|
|
95
|
+
* @param {CSN.Definition} target
|
|
96
|
+
* @param {CSN.Definition} source
|
|
97
|
+
*/
|
|
98
|
+
function propagateDefProps(target, source) {
|
|
99
|
+
const keys = Object.keys(source);
|
|
100
|
+
for (const prop of keys) {
|
|
101
|
+
// do not overwrite properties in target def
|
|
102
|
+
if (!(prop in target)) {
|
|
103
|
+
const func = defProps[prop] || defProps[prop.charAt(0)];
|
|
104
|
+
if (func) func(prop, target, source);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Propagate properties from Universal to Client CSN relevant for members.
|
|
111
|
+
*
|
|
112
|
+
* @param {CSN.Artifact} target
|
|
113
|
+
*/
|
|
114
|
+
function propagateMembersProps(target) {
|
|
115
|
+
// TODO: in the future, consider case when target is a member itself,
|
|
116
|
+
// for when we do not run with the complete CSN
|
|
117
|
+
// TODO: use newly added 'forEachMemberRecursivelyWithQuery'
|
|
118
|
+
forEachMemberRecursively(target, (member) => {
|
|
119
|
+
propagateMemberPropsFromOrigin(member);
|
|
120
|
+
if (member.target && !member.keys && !member.on)
|
|
121
|
+
calculateForeignKeys(member);
|
|
122
|
+
});
|
|
123
|
+
target.query && forAllQueries(target.query, (query) => {
|
|
124
|
+
if (query.SELECT && query.SELECT.elements) {
|
|
125
|
+
forEachMember(query.SELECT, (member) => {
|
|
126
|
+
propagateMemberPropsFromOrigin(member);
|
|
127
|
+
if (member.target && !member.keys && !member.on)
|
|
128
|
+
calculateForeignKeys(member);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
setProp(target, '_status', 'propagated');
|
|
30
133
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
134
|
+
|
|
135
|
+
function propagateMemberPropsFromOrigin(member) {
|
|
136
|
+
// For empty members (`{}`), the origin was set in a previous call to `getOrigin(definition)`.
|
|
137
|
+
const memberOrigin = getOrigin(member);
|
|
138
|
+
if (!memberOrigin) return;
|
|
139
|
+
|
|
140
|
+
// when having an element with a type property that is
|
|
141
|
+
// user-defined there is no need to propagate 'elements',
|
|
142
|
+
// 'kind', etc. from the origin (which is the type definition)
|
|
143
|
+
if (member.type && Object.keys(member).length === 1) return;
|
|
144
|
+
|
|
145
|
+
const keys = Object.keys(memberOrigin);
|
|
146
|
+
// Copy over properties from the origin element.
|
|
147
|
+
// Don't propagate "kind" as this property is not allowed in elements.
|
|
148
|
+
for (const key of keys) {
|
|
149
|
+
if (!(key in member) && key !== 'kind')
|
|
150
|
+
member[key] = memberOrigin[key];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// copy over own annotations/properties
|
|
154
|
+
// TODO: try to create use-/test-case where this needs
|
|
155
|
+
// to be applied on definition level, ATM it is done only for members
|
|
156
|
+
if (member.$origin && !Array.isArray(member.$origin) && member.$origin.$origin) {
|
|
157
|
+
const ownKeys = Object.keys(member.$origin);
|
|
158
|
+
for (const key of ownKeys) {
|
|
159
|
+
if (key !== '$origin')
|
|
160
|
+
member[key] = member.$origin[key];
|
|
43
161
|
}
|
|
162
|
+
}
|
|
44
163
|
|
|
164
|
+
// In case of managed composition an anonymous $origin is used.
|
|
165
|
+
// csnRefs::getOrigin returns {} for such a member, thus have to recreate the client CSN from
|
|
166
|
+
// the values in the $origin. PR #8072
|
|
167
|
+
if (!Object.keys(memberOrigin).length && member.$origin && member.$origin.type === 'cds.Composition') {
|
|
168
|
+
member.type = member.$origin.type;
|
|
169
|
+
member.cardinality = member.$origin.cardinality;
|
|
170
|
+
member.targetAspect = member.$origin.target;
|
|
45
171
|
}
|
|
46
172
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return;
|
|
173
|
+
|
|
174
|
+
function calculateForeignKeys(member) {
|
|
175
|
+
// managed assocs in universal CSN have no longer keys
|
|
176
|
+
// if they are not explicitly defined - PR#8064
|
|
177
|
+
const target = artifactRef(member.target);
|
|
178
|
+
const targetKeys = Object.keys(target.elements).filter((key) => target.elements[key].key);
|
|
179
|
+
member.keys = targetKeys.map(
|
|
180
|
+
keyName => { return { ref: [keyName] } }
|
|
181
|
+
);
|
|
57
182
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @cds.autoexpose for example, is propagated only if at definition level and only if
|
|
188
|
+
* the primary source (left-most) does not follow an association.
|
|
189
|
+
*
|
|
190
|
+
* @param {String} prop
|
|
191
|
+
* @param {CSN.Definition} target
|
|
192
|
+
* @param {CSN.Definition} source
|
|
193
|
+
*/
|
|
194
|
+
function onlyViaArtifact(prop, target, source) {
|
|
195
|
+
if (!target.kind) return;
|
|
196
|
+
const primarySourceRef = getQueryPrimarySource(target.query || target.projection);
|
|
197
|
+
const artRef = primarySourceRef ? artifactRef(primarySourceRef) : source;
|
|
198
|
+
if (!artRef.target) {
|
|
199
|
+
target[prop] = source[prop];
|
|
63
200
|
}
|
|
64
|
-
|
|
65
|
-
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Execute only if the target definition is a user-defined type.
|
|
205
|
+
*
|
|
206
|
+
* @param {String} prop
|
|
207
|
+
* @param {CSN.Definition} target
|
|
208
|
+
* @param {CSN.Definition} source
|
|
209
|
+
* @returns
|
|
210
|
+
*/
|
|
211
|
+
function onlyTypeDef(prop, target, source) {
|
|
212
|
+
if (target.kind !== 'type') return;
|
|
213
|
+
target[prop] = source[prop];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Removes every occurrence of '$origin' and '$generated'
|
|
218
|
+
* for compatibility with what we have in client CSN.
|
|
219
|
+
*
|
|
220
|
+
* @param {CSN.Model} csn
|
|
221
|
+
*/
|
|
222
|
+
function removeDollarProperties(csn) {
|
|
223
|
+
forEachDefinition(csn, (def) => {
|
|
224
|
+
delete def.$origin;
|
|
225
|
+
delete def.$generated;
|
|
226
|
+
// TODO: use newly added 'forEachMemberRecursivelyWithQuery'
|
|
227
|
+
forEachMemberRecursively(def, (member) => delete member.$origin);
|
|
228
|
+
def.query && forAllQueries(def.query, (query) => {
|
|
229
|
+
if (query.SELECT && query.SELECT.elements) {
|
|
230
|
+
forEachMember(query.SELECT, (member) => {
|
|
231
|
+
delete member.$origin;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
})
|
|
66
236
|
}
|
|
67
237
|
}
|
package/lib/utils/file.js
CHANGED
|
@@ -7,7 +7,7 @@ const util = require('util');
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Split the given source string into its lines. Respects Unix,
|
|
10
|
-
* Windows
|
|
10
|
+
* Windows and Macintosh line breaks.
|
|
11
11
|
*
|
|
12
12
|
* @param {string} src
|
|
13
13
|
* @returns {string[]}
|
|
@@ -48,6 +48,7 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
|
+
/** @type {function(string, string)} */
|
|
51
52
|
readFileAsync: util.promisify(readFile),
|
|
52
53
|
readFile,
|
|
53
54
|
readFileSync,
|
package/lib/utils/timetrace.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* A single
|
|
4
|
+
* A single StopWatch encapsulates the runtime of a selected code frame.
|
|
5
5
|
*
|
|
6
6
|
* @class TimeTrace
|
|
7
7
|
*/
|
|
@@ -14,6 +14,8 @@ class StopWatch {
|
|
|
14
14
|
*/
|
|
15
15
|
constructor(id) {
|
|
16
16
|
this.id = id;
|
|
17
|
+
// TODO: If we require Node 12, use process.hrtime.bigint()
|
|
18
|
+
// as process.hrtime() is deprecated.
|
|
17
19
|
// eslint-disable-next-line no-multi-assign
|
|
18
20
|
this.startTime = this.lapTime = process.hrtime();
|
|
19
21
|
}
|
|
@@ -123,4 +125,8 @@ const ignoreTimeTrace = {
|
|
|
123
125
|
};
|
|
124
126
|
|
|
125
127
|
const doTimeTrace = process && process.env && process.env.CDSC_TIMETRACING !== undefined;
|
|
126
|
-
module.exports = {
|
|
128
|
+
module.exports = {
|
|
129
|
+
timetrace: (doTimeTrace ? new TimeTracer() : ignoreTimeTrace),
|
|
130
|
+
TimeTracer,
|
|
131
|
+
StopWatch,
|
|
132
|
+
};
|