@sap/cds-compiler 6.7.3 → 6.8.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 +28 -0
- package/bin/cdsc.js +5 -5
- package/bin/cdsse.js +1 -1
- package/lib/api/main.js +9 -8
- package/lib/api/options.js +2 -1
- package/lib/api/validate.js +1 -1
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +7 -2
- package/lib/base/messages.js +2 -2
- package/lib/{optionProcessor.js → base/optionProcessor.js} +3 -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/builtins.js +1 -1
- package/lib/compiler/checks.js +3 -3
- package/lib/compiler/define.js +5 -2
- package/lib/{base → compiler}/dictionaries.js +2 -0
- package/lib/compiler/extend.js +2 -2
- 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/populate.js +2 -2
- package/lib/compiler/resolve.js +4 -2
- package/lib/compiler/shared.js +35 -6
- package/lib/compiler/utils.js +2 -4
- package/lib/compiler/xpr-rewrite.js +1 -1
- package/lib/edm/annotations/edmJson.js +2 -4
- package/lib/edm/annotations/genericTranslation.js +2 -1
- 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 +1 -12
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1068 -1067
- package/lib/json/from-csn.js +7 -2
- package/lib/json/to-csn.js +17 -2
- 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 +40 -3
- package/lib/parsers/index.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +3 -3
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toSql.js +7 -7
- package/lib/render/utils/common.js +9 -2
- 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 +2 -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 +1 -1
- package/lib/transform/effective/main.js +19 -4
- package/lib/transform/effective/types.js +1 -1
- package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
- package/lib/transform/forOdata.js +5 -5
- package/lib/transform/forRelationalDB.js +41 -9
- 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 +4 -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 +2 -2
- /package/lib/{model → base}/cloneCsn.js +0 -0
- /package/lib/{model → base}/csnRefs.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/sql-identifier.js
CHANGED
|
@@ -57,18 +57,21 @@ const sqlDialects = {
|
|
|
57
57
|
reservedWords: keywords.postgres,
|
|
58
58
|
effectiveName: name => name.toLowerCase(),
|
|
59
59
|
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
|
|
60
|
+
maxIdentifierLength: 63,
|
|
60
61
|
},
|
|
61
62
|
hana: {
|
|
62
63
|
regularRegex: /^[A-Za-z_][A-Za-z_$#0-9]*$/,
|
|
63
64
|
reservedWords: keywords.hana,
|
|
64
65
|
effectiveName: name => name.toUpperCase(),
|
|
65
66
|
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
|
|
67
|
+
maxIdentifierLength: 127,
|
|
66
68
|
},
|
|
67
69
|
hdbcds: {
|
|
68
70
|
regularRegex: /^[A-Za-z_][A-Za-z_0-9]*$/,
|
|
69
71
|
reservedWords: keywords.hdbcds,
|
|
70
72
|
effectiveName: name => name,
|
|
71
73
|
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
|
|
74
|
+
maxIdentifierLength: 127,
|
|
72
75
|
},
|
|
73
76
|
};
|
|
74
77
|
|
|
@@ -98,4 +101,9 @@ function delimitedId( name, dialect ) {
|
|
|
98
101
|
return s.asDelimitedId( name );
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
module.exports = {
|
|
104
|
+
module.exports = {
|
|
105
|
+
smartId,
|
|
106
|
+
smartFuncId,
|
|
107
|
+
delimitedId,
|
|
108
|
+
sqlDialects,
|
|
109
|
+
};
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
|
|
40
40
|
'use strict';
|
|
41
41
|
|
|
42
|
-
const { csnRefs, artifactProperties } = require('
|
|
42
|
+
const { csnRefs, artifactProperties } = require('../base/csnRefs');
|
|
43
43
|
const { locationString } = require('../base/location');
|
|
44
44
|
const { CompilerAssertion } = require('../base/error');
|
|
45
45
|
const { isAnnotationExpression } = require('../base/builtins');
|
|
46
|
-
const shuffleGen = require('../
|
|
46
|
+
const shuffleGen = require('../utils/shuffle');
|
|
47
47
|
|
|
48
48
|
function enrichCsn( csn, options = {} ) {
|
|
49
49
|
const transformers = {
|
|
@@ -283,7 +283,8 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
function revealSingleExtension( node, parent ) {
|
|
286
|
-
return (node.kind && node.__unique_id__ == null &&
|
|
286
|
+
return (node.kind && node.__unique_id__ == null &&
|
|
287
|
+
node.$effectiveSeqNo == null && !node.builtin)
|
|
287
288
|
? reveal( node, parent )
|
|
288
289
|
: artifactIdentifier( node, parent );
|
|
289
290
|
}
|
|
@@ -78,7 +78,7 @@ function processAssertUnique( csn, options, messageFunctions ) {
|
|
|
78
78
|
// constraintKey is the concatenation of all flattened paths (order is important)
|
|
79
79
|
let constraintKey = '';
|
|
80
80
|
flattenedPathObjects.forEach((p) => {
|
|
81
|
-
const pstr = p.ref.map(path => path.id).join('.');
|
|
81
|
+
const pstr = p.ref.map(path => path.id || path).join('.');
|
|
82
82
|
constraintKey += pstr;
|
|
83
83
|
if (!pathxrefs[pstr])
|
|
84
84
|
pathxrefs[pstr] = 1;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const {
|
|
4
4
|
forAllQueries, forEachDefinition, walkCsnPath, transformExpression,
|
|
5
5
|
} = require('../../../model/csnUtils');
|
|
6
|
-
const { setProp } = require('../../../
|
|
6
|
+
const { setProp } = require('../../../utils/objectUtils');
|
|
7
7
|
const { getHelpers } = require('./utils');
|
|
8
8
|
|
|
9
9
|
const normalizeFromLeaf = require('./normalizeFrom');
|
|
@@ -4,9 +4,9 @@ const {
|
|
|
4
4
|
applyTransformationsOnNonDictionary, isAssociationOperand, isDollarSelfOrProjectionOperand,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
|
|
7
|
-
const { setProp } = require('../../
|
|
7
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
8
8
|
const { forEach } = require('../../utils/objectUtils');
|
|
9
|
-
const { cloneCsnNonDict } = require('../../
|
|
9
|
+
const { cloneCsnNonDict } = require('../../base/cloneCsn');
|
|
10
10
|
const { ModelError } = require('../../base/error');
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -6,8 +6,8 @@ const {
|
|
|
6
6
|
getUtils,
|
|
7
7
|
forEachDefinition,
|
|
8
8
|
} = require('../../model/csnUtils');
|
|
9
|
-
const { implicitAs, columnAlias, pathId } = require('../../
|
|
10
|
-
const { setProp } = require('../../
|
|
9
|
+
const { implicitAs, columnAlias, pathId } = require('../../base/csnRefs');
|
|
10
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
11
11
|
const { killNonrequiredAnno } = require('./killAnnotations');
|
|
12
12
|
const { featureFlags } = require('../featureFlags');
|
|
13
13
|
const { applyTransformationsOnNonDictionary } = require('./applyTransformations');
|
|
@@ -9,11 +9,10 @@ const {
|
|
|
9
9
|
} = require('../../model/csnUtils');
|
|
10
10
|
const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
|
|
11
11
|
const transformUtils = require('../transformUtils');
|
|
12
|
-
const { csnRefs } = require('../../
|
|
13
|
-
const { setProp } = require('../../
|
|
14
|
-
const { forEach } = require('../../utils/objectUtils');
|
|
12
|
+
const { csnRefs } = require('../../base/csnRefs');
|
|
13
|
+
const { setProp, forEach } = require('../../utils/objectUtils');
|
|
15
14
|
const { transformExpression } = require('./applyTransformations');
|
|
16
|
-
const { cloneCsnNonDict } = require('../../
|
|
15
|
+
const { cloneCsnNonDict } = require('../../base/cloneCsn');
|
|
17
16
|
const { EdmTypeFacetNames } = require('../../edm/EdmPrimitiveTypeDefinitions');
|
|
18
17
|
const { adaptAnnotationsRefs } = require('../odata/adaptAnnotationRefs');
|
|
19
18
|
|
|
@@ -27,6 +27,7 @@ const requiredAnnos = {
|
|
|
27
27
|
[sqlServiceAnnotation]: true,
|
|
28
28
|
'@cds.external': true, // for external ABAP SQL services and data products for now
|
|
29
29
|
'@data.product': true, // for data product production
|
|
30
|
+
'@hierarchy': true, // for Fiori Tree Views
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -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 { hasPersistenceSkipAnnotation } = require('../../model/csnUtils');
|
|
5
6
|
|
|
6
7
|
const sqlServiceAnnotation = '@protocol';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp } = require('../../
|
|
3
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
4
4
|
const { CompilerAssertion } = require('../../base/error');
|
|
5
5
|
const {
|
|
6
6
|
forEachDefinition,
|
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
implicitAs,
|
|
10
10
|
} = require('../../model/csnUtils');
|
|
11
11
|
const { getBranches } = require('./flattening');
|
|
12
|
-
const { cloneCsnNonDict } = require('../../
|
|
12
|
+
const { cloneCsnNonDict } = require('../../base/cloneCsn');
|
|
13
13
|
|
|
14
14
|
const cloneCsnOptions = { hiddenPropertiesToClone: [ '_art', '_links', '$env', '$scope' ] };
|
|
15
15
|
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
const {
|
|
4
4
|
getNormalizedQuery, forEachMember,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
|
-
const { implicitAs } = require('../../
|
|
7
|
-
const { setProp
|
|
6
|
+
const { implicitAs } = require('../../base/csnRefs');
|
|
7
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
8
|
+
const { isBetaEnabled } = require('../../base/specialOptions');
|
|
8
9
|
const { getTransformers } = require('../transformUtils');
|
|
9
10
|
|
|
10
11
|
const validToString = '@cds.valid.to';
|
|
@@ -206,9 +207,16 @@ function getAnnotationHandler( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
206
207
|
'Expecting $(NAME) and $(ID) if $(ANNO) is used');
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
|
|
210
|
+
// Collect paths for unique constraint (original keys + validFrom)
|
|
211
|
+
const uniquePaths = [];
|
|
212
|
+
const addedMembers = new Set();
|
|
213
|
+
|
|
214
|
+
forEachMember(artifact, (member, memberName) => {
|
|
210
215
|
if (member.key) {
|
|
211
|
-
|
|
216
|
+
// Add to unique constraint paths - use the same ref format as assertUnique.prepare does
|
|
217
|
+
// this can then be used by assertUnique.rewrite to process
|
|
218
|
+
uniquePaths.push({ ref: [ { id: memberName } ], _art: member });
|
|
219
|
+
addedMembers.add(memberName);
|
|
212
220
|
delete member.key;
|
|
213
221
|
// Remember that this element was a key in the original artifact.
|
|
214
222
|
// This is needed for localized convenience view generation.
|
|
@@ -220,8 +228,25 @@ function getAnnotationHandler( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
220
228
|
});
|
|
221
229
|
|
|
222
230
|
validFrom.forEach((member) => {
|
|
223
|
-
|
|
231
|
+
// Add validFrom to unique constraint paths (if not already added as a key)
|
|
232
|
+
const memberName = member.path[member.path.length - 1];
|
|
233
|
+
if (!addedMembers.has(memberName))
|
|
234
|
+
uniquePaths.push({ ref: [ { id: memberName } ], _art: member.element });
|
|
224
235
|
});
|
|
236
|
+
|
|
237
|
+
// Create $tableConstraints.unique entry - will be rewritten by assertUnique.rewrite()
|
|
238
|
+
if (uniquePaths.length > 0) {
|
|
239
|
+
if (!artifact.$tableConstraints)
|
|
240
|
+
artifact.$tableConstraints = Object.create(null);
|
|
241
|
+
if (!artifact.$tableConstraints.unique)
|
|
242
|
+
artifact.$tableConstraints.unique = Object.create(null);
|
|
243
|
+
|
|
244
|
+
// Use a named constraint
|
|
245
|
+
artifact.$tableConstraints.unique.__c_cds_valid = {
|
|
246
|
+
paths: uniquePaths,
|
|
247
|
+
parentTable: artifactName,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
225
250
|
}
|
|
226
251
|
else {
|
|
227
252
|
validFrom.forEach((member) => {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
getUtils, applyTransformationsOnNonDictionary,
|
|
4
|
+
getUtils, applyTransformationsOnNonDictionary,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
|
-
const { implicitAs, columnAlias, pathId } = require('../../
|
|
6
|
+
const { implicitAs, columnAlias, pathId } = require('../../base/csnRefs');
|
|
7
7
|
const { ModelError } = require('../../base/error');
|
|
8
|
-
const { setProp } = require('../../
|
|
9
|
-
const { cloneCsnNonDict } = require('../../
|
|
8
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
9
|
+
const { cloneCsnNonDict } = require('../../base/cloneCsn');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* If a mixin association is published, return the mixin association.
|
|
@@ -472,7 +472,7 @@ function getColumnMap( query, csnUtils ) {
|
|
|
472
472
|
|
|
473
473
|
|
|
474
474
|
/**
|
|
475
|
-
* Ensure that
|
|
475
|
+
* Ensure that a single query's columns have names. A column does not have
|
|
476
476
|
* a name if the column is an expression and there is no explicit alias.
|
|
477
477
|
* In that case an internal alias (from csnRefs()) is used and made explicit
|
|
478
478
|
* via non-enumerable `as`.
|
|
@@ -485,24 +485,20 @@ function getColumnMap( query, csnUtils ) {
|
|
|
485
485
|
* - We can't use e.g. `$as`, as csnRefs() does not use that property, and it must not
|
|
486
486
|
* invent another name for the column (could happen after flattening).
|
|
487
487
|
*
|
|
488
|
-
* @param {
|
|
489
|
-
* @param {CSN.Options} options
|
|
488
|
+
* @param {object} query
|
|
490
489
|
* @param {object} csnUtils
|
|
490
|
+
* @param {CSN.Options} options
|
|
491
491
|
*/
|
|
492
|
-
function ensureColumnNames(
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
else
|
|
501
|
-
setProp(col, 'as', csnUtils.getColumnName(col));
|
|
502
|
-
}
|
|
503
|
-
}
|
|
492
|
+
function ensureColumnNames( query, csnUtils, options ) {
|
|
493
|
+
const select = query.SELECT || query.projection;
|
|
494
|
+
for (const col of select?.columns || []) {
|
|
495
|
+
if (col !== '*' && !columnAlias(col)) {
|
|
496
|
+
if (options.transformation === 'hdbcds')
|
|
497
|
+
col.as = csnUtils.getColumnName(col);
|
|
498
|
+
else
|
|
499
|
+
setProp(col, 'as', csnUtils.getColumnName(col));
|
|
504
500
|
}
|
|
505
|
-
}
|
|
501
|
+
}
|
|
506
502
|
}
|
|
507
503
|
|
|
508
504
|
module.exports = {
|
|
@@ -4,10 +4,9 @@ const {
|
|
|
4
4
|
getServiceNames, forEachDefinition,
|
|
5
5
|
getResultingName, forEachMemberRecursively, applyAnnotationsFromExtensions,
|
|
6
6
|
} = require('../../model/csnUtils');
|
|
7
|
-
const { setProp } = require('../../base/model');
|
|
8
7
|
const { getTransformers } = require('../transformUtils');
|
|
9
8
|
const { ModelError } = require('../../base/error');
|
|
10
|
-
const { forEach } = require('../../utils/objectUtils');
|
|
9
|
+
const { setProp, forEach } = require('../../utils/objectUtils');
|
|
11
10
|
const draftAnnotation = '@odata.draft.enabled';
|
|
12
11
|
const booleanBuiltin = 'cds.Boolean';
|
|
13
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp } = require('../../
|
|
3
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
4
4
|
const flattening = require('../db/flattening');
|
|
5
5
|
const {
|
|
6
6
|
applyTransformations, forEachDefinition, forEachMemberRecursively, forEachMember, applyTransformationsOnNonDictionary,
|
|
@@ -4,7 +4,7 @@ const {
|
|
|
4
4
|
forEachDefinition, forEachMemberRecursively, applyTransformationsOnNonDictionary, transformExpression, transformAnnotationExpression,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
const { getStructStepsFlattener } = require('../db/flattening');
|
|
7
|
-
const { setProp } = require('../../
|
|
7
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
8
8
|
const { forEach } = require('../../utils/objectUtils');
|
|
9
9
|
|
|
10
10
|
|
|
@@ -16,11 +16,13 @@ const handleExists = require('../db/assocsToQueries/transformExists');
|
|
|
16
16
|
const misc = require('./misc');
|
|
17
17
|
const annotations = require('./annotations');
|
|
18
18
|
const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
|
|
19
|
-
const { cloneFullCsn } = require('../../
|
|
19
|
+
const { cloneFullCsn } = require('../../base/cloneCsn');
|
|
20
20
|
const { featureFlags, removeFeatureFlags } = require('../featureFlags');
|
|
21
21
|
const getServiceFilterFunction = require('./service');
|
|
22
|
-
const { traverseQuery } = require('../../
|
|
22
|
+
const { traverseQuery } = require('../../base/csnRefs');
|
|
23
23
|
const { expandWildcard } = require('../db/expansion');
|
|
24
|
+
const { getQueryFeatureFlagSetter, getDefinitionFeatureFlagSetter } = require('../../checks/featureFlags');
|
|
25
|
+
const shuffleGen = require('../../utils/shuffle');
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* This is just a PoC for now!
|
|
@@ -40,6 +42,10 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
40
42
|
delete csn.vocabularies; // must not be set for effective CSN
|
|
41
43
|
messageFunctions.setModel(csn);
|
|
42
44
|
|
|
45
|
+
const { shuffleDict } = shuffleGen(options.testMode);
|
|
46
|
+
if (options.testMode && csn.definitions)
|
|
47
|
+
csn.definitions = shuffleDict(csn.definitions);
|
|
48
|
+
|
|
43
49
|
const transformerUtils = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
44
50
|
const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
|
|
45
51
|
|
|
@@ -48,9 +54,17 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
48
54
|
let { csnUtils } = transformerUtils;
|
|
49
55
|
csnUtils.initAllDefinitions();
|
|
50
56
|
|
|
57
|
+
const setQueryFeatureFlags = getQueryFeatureFlagSetter(csn);
|
|
58
|
+
const setDefinitionFeatureFlags = getDefinitionFeatureFlagSetter(csn, options);
|
|
59
|
+
|
|
51
60
|
forEachDefinition(csn, (def) => {
|
|
52
|
-
if (def.query || def.projection)
|
|
53
|
-
traverseQuery(def.query || def, null, null, query =>
|
|
61
|
+
if (def.query || def.projection) {
|
|
62
|
+
traverseQuery(def.query || def, null, null, (query) => {
|
|
63
|
+
expandWildcard(query, csnUtils, options);
|
|
64
|
+
setQueryFeatureFlags(query);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
setDefinitionFeatureFlags(def);
|
|
54
68
|
});
|
|
55
69
|
|
|
56
70
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
@@ -94,6 +108,7 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
94
108
|
transformerUtils.csnUtils = getUtils(csn, 'init-all');
|
|
95
109
|
associations.managedToUnmanaged(csn, options, transformerUtils, messageFunctions);
|
|
96
110
|
associations.transformBacklinks(csn, options, transformerUtils, messageFunctions);
|
|
111
|
+
|
|
97
112
|
const transformers = mergeTransformers([
|
|
98
113
|
options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {},
|
|
99
114
|
misc.removeDefinitionsAndProperties(csn, options),
|
|
@@ -6,7 +6,7 @@ const {
|
|
|
6
6
|
applyTransformationsOnDictionary,
|
|
7
7
|
} = require('../../model/csnUtils');
|
|
8
8
|
const { forEachKey } = require('../../utils/objectUtils');
|
|
9
|
-
const { cloneCsnDict, cloneCsnNonDict } = require('../../
|
|
9
|
+
const { cloneCsnDict, cloneCsnNonDict } = require('../../base/cloneCsn');
|
|
10
10
|
const propertiesToSkipForCasts = { enum: true };
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isBuiltinType } = require('
|
|
3
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
4
4
|
/**
|
|
5
5
|
* Processing the `@hierarchy` annotation on projections/views and generates:
|
|
6
6
|
* - the OData annotations: `@Aggregation.RecursiveHierarchy`, `@Hierarchy.RecursiveHierarchy`
|
|
@@ -16,17 +16,22 @@ const { isBuiltinType } = require('../../base/builtins');
|
|
|
16
16
|
* - the foreign key is of scalar type
|
|
17
17
|
* Otherwise, appropriate warnings/errors are raised.
|
|
18
18
|
*/
|
|
19
|
-
function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers) {
|
|
19
|
+
function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers, options) {
|
|
20
20
|
const { error, warning } = messageFunctions;
|
|
21
|
-
const {
|
|
21
|
+
const { isAssociation } = csnUtils;
|
|
22
22
|
const { setAnnotation, addElement, createScalarElement } = transformers;
|
|
23
|
+
const isODataTransformation = options.transformation === 'odata';
|
|
23
24
|
|
|
24
|
-
if (Object.keys(def).some(key => key.startsWith('@hierarchy#')))
|
|
25
|
-
|
|
25
|
+
if (Object.keys(def).some(key => key.startsWith('@hierarchy#'))) {
|
|
26
|
+
if (isODataTransformation)
|
|
27
|
+
error(null, [ 'definitions', defName ], {}, 'Assigning qualifier with the @hierarchy annotation is not supported');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
26
30
|
|
|
27
31
|
// supported only on projections or views
|
|
28
32
|
if (!(def.projection || def.query)) {
|
|
29
|
-
|
|
33
|
+
if (isODataTransformation)
|
|
34
|
+
warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy is only supported on projections and views');
|
|
30
35
|
return;
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -34,7 +39,8 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
34
39
|
const mngAssocsToSelf = [];
|
|
35
40
|
const unmgAssocsToSelf = [];
|
|
36
41
|
Object.entries(def.elements).forEach(([ name, elem ]) => {
|
|
37
|
-
if (isManagedAssociation(elem) && elem.target === defName && ![ 'DraftAdministrativeData' ].includes(name))
|
|
42
|
+
// if (isManagedAssociation(elem) && elem.target === defName && ![ 'DraftAdministrativeData' ].includes(name))
|
|
43
|
+
if (isAssociation(elem) && elem.keys && elem.target === defName && ![ 'DraftAdministrativeData' ].includes(name))
|
|
38
44
|
mngAssocsToSelf.push([ name, elem ]);
|
|
39
45
|
else if (isAssociation(elem) && elem.on &&
|
|
40
46
|
elem.target === defName && ![ 'SiblingEntity' ].includes(name))
|
|
@@ -45,19 +51,19 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
45
51
|
// association pointing to self and has a single foreign key that is of scalar type
|
|
46
52
|
if (typeof def['@hierarchy'] === 'boolean' && def['@hierarchy'] === true) {
|
|
47
53
|
if (mngAssocsToSelf.length > 1) {
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
if (isODataTransformation)
|
|
55
|
+
warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy with value true is ignored as multiple managed associations to self exist');
|
|
50
56
|
return;
|
|
51
57
|
}
|
|
52
58
|
else if (mngAssocsToSelf.length === 0) {
|
|
53
59
|
if (unmgAssocsToSelf.length === 1) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
warning(null, [ 'definitions', defName ], {},
|
|
59
|
-
'Annotation @hierarchy with value true is ignored as no managed association to self exists');
|
|
60
|
+
if (isODataTransformation) {
|
|
61
|
+
error(null, unmgAssocsToSelf[0][1].$path || [ 'definitions', defName, 'elements', unmgAssocsToSelf[0][0] ],
|
|
62
|
+
{ name: unmgAssocsToSelf[0][0] }, 'Annotation @hierarchy is not supported for unmanaged association $(NAME)');
|
|
63
|
+
}
|
|
60
64
|
}
|
|
65
|
+
if (isODataTransformation)
|
|
66
|
+
warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy with value true is ignored as no managed association to self exists');
|
|
61
67
|
return;
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -65,7 +71,8 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
65
71
|
const assocName = mngAssocsToSelf[0][0];
|
|
66
72
|
|
|
67
73
|
if (isValidHierarchyAssociation(assoc, assocName)) {
|
|
68
|
-
|
|
74
|
+
if (isODataTransformation)
|
|
75
|
+
addHierarchyAnnotations(def, defName, assoc.keys[0].ref.join(), assocName);
|
|
69
76
|
addHierarchyFields(def, defName);
|
|
70
77
|
}
|
|
71
78
|
}
|
|
@@ -73,19 +80,22 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
73
80
|
const assocName = def['@hierarchy']['='];
|
|
74
81
|
const unmngAssoc = unmgAssocsToSelf.find(a => a[0] === assocName);
|
|
75
82
|
if (unmngAssoc) {
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
if (isODataTransformation) {
|
|
84
|
+
error(null, unmngAssoc[1].$path || [ 'definitions', defName, 'elements', unmngAssoc[0] ],
|
|
85
|
+
{ name: unmngAssoc[0] }, 'Annotation @hierarchy is not supported for unmanaged association $(NAME)');
|
|
86
|
+
}
|
|
78
87
|
return;
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
const assoc = mngAssocsToSelf.find(a => a[0] === assocName);
|
|
82
91
|
if (!assoc) {
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
if (isODataTransformation)
|
|
93
|
+
warning(null, [ 'definitions', defName ], { name: assocName }, 'Annotation @hierarchy refers to a non-existing managed association $(NAME)');
|
|
85
94
|
return;
|
|
86
95
|
}
|
|
87
96
|
if (isValidHierarchyAssociation(assoc[1], assocName)) {
|
|
88
|
-
|
|
97
|
+
if (isODataTransformation)
|
|
98
|
+
addHierarchyAnnotations(def, defName, assoc[1].keys[0].ref.join(), assocName);
|
|
89
99
|
addHierarchyFields(def, defName);
|
|
90
100
|
}
|
|
91
101
|
}
|
|
@@ -94,16 +104,20 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
94
104
|
function isValidHierarchyAssociation(assoc, assocName) {
|
|
95
105
|
// the association must have exactly one scalar foreign key
|
|
96
106
|
if (assoc.keys.length > 1) {
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
if (isODataTransformation) {
|
|
108
|
+
warning(null, assoc.$path || [ 'definitions', defName, 'elements', assocName ], { name: assocName },
|
|
109
|
+
'Annotation @hierarchy is ignored as the managed association $(NAME) has multiple foreign keys');
|
|
110
|
+
}
|
|
99
111
|
return false;
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
const fkName = assoc.keys[0].ref.join();
|
|
103
115
|
const defKey = Object.entries(def.elements).find(([ name, elem ]) => elem.key && name === fkName);
|
|
104
116
|
if (defKey && !isBuiltinType(defKey[1].type)) {
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
if (isODataTransformation) {
|
|
118
|
+
warning(null, assoc.$path || [ 'definitions', defName, 'elements', assocName ], { name: assocName },
|
|
119
|
+
'Annotation @hierarchy is ignored as the foreign key of the managed association $(NAME) is not of a scalar type');
|
|
120
|
+
}
|
|
107
121
|
return false;
|
|
108
122
|
}
|
|
109
123
|
return true;
|
|
@@ -143,6 +157,7 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
143
157
|
}
|
|
144
158
|
|
|
145
159
|
function addHierarchyFields(def, defName) {
|
|
160
|
+
// add elements in elements dictionary
|
|
146
161
|
const limitedDescendantCount = createScalarElement('LimitedDescendantCount', 'cds.Integer');
|
|
147
162
|
limitedDescendantCount.LimitedDescendantCount['@Core.Computed'] = true;
|
|
148
163
|
limitedDescendantCount.LimitedDescendantCount.$calc = { val: null };
|
|
@@ -162,6 +177,14 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
|
|
|
162
177
|
limitedRank.LimitedRank['@Core.Computed'] = true;
|
|
163
178
|
limitedRank.LimitedRank.$calc = { val: null };
|
|
164
179
|
addElement(limitedRank, def, defName);
|
|
180
|
+
|
|
181
|
+
// add fields in the query columns
|
|
182
|
+
if (def.query && def.query.SELECT && def.query.SELECT.columns) {
|
|
183
|
+
def.query.SELECT.columns.push({ val: null, as: 'LimitedDescendantCount', cast: { type: 'cds.Integer' } });
|
|
184
|
+
def.query.SELECT.columns.push({ val: null, as: 'DistanceFromRoot', cast: { type: 'cds.Integer' } });
|
|
185
|
+
def.query.SELECT.columns.push({ val: null, as: 'DrillState', cast: { type: 'cds.String' } });
|
|
186
|
+
def.query.SELECT.columns.push({ val: null, as: 'LimitedRank', cast: { type: 'cds.Integer' } });
|
|
187
|
+
}
|
|
165
188
|
}
|
|
166
189
|
|
|
167
190
|
function checkAndReportErrorWhenAnnotationExists(def, defName, anno) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isBetaEnabled } = require('../base/
|
|
3
|
+
const { isBetaEnabled } = require('../base/specialOptions');
|
|
4
4
|
const transformUtils = require('./transformUtils');
|
|
5
5
|
const {
|
|
6
6
|
forEachDefinition,
|
|
@@ -22,15 +22,15 @@ const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./oda
|
|
|
22
22
|
const expandToFinalBaseType = require('./odata/toFinalBaseType');
|
|
23
23
|
const flattening = require('./odata/flattening');
|
|
24
24
|
const createForeignKeyElements = require('./odata/createForeignKeys');
|
|
25
|
-
const generateFioriTreeViewAnnotationsAndFields = require('./
|
|
25
|
+
const generateFioriTreeViewAnnotationsAndFields = require('./fioriTreeViews');
|
|
26
26
|
const associations = require('./db/associations');
|
|
27
27
|
const expansion = require('./db/expansion');
|
|
28
28
|
const generateDrafts = require('./draft/odata');
|
|
29
29
|
|
|
30
30
|
const { addTenantFields } = require('./addTenantFields');
|
|
31
31
|
const { addLocalizationViews } = require('./localized');
|
|
32
|
-
const { cloneFullCsn } = require('../
|
|
33
|
-
const { csnRefs } = require('../
|
|
32
|
+
const { cloneFullCsn } = require('../base/cloneCsn');
|
|
33
|
+
const { csnRefs } = require('../base/csnRefs');
|
|
34
34
|
const replaceForeignKeyRefsInExpressionAnnotations = require('./odata/foreignKeyRefsInXprAnnos');
|
|
35
35
|
const { isAnnotationExpression, xprInAnnoProperties } = require('../base/builtins');
|
|
36
36
|
|
|
@@ -297,7 +297,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
297
297
|
|
|
298
298
|
// Generate annotations and fields needed for the Fiori Tree Views out of the @hierarchy annotation
|
|
299
299
|
if (def['@hierarchy'])
|
|
300
|
-
generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers);
|
|
300
|
+
generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers, options);
|
|
301
301
|
|
|
302
302
|
// Annotate artifacts with their DB names if requested.
|
|
303
303
|
// Skip artifacts that have no DB equivalent anyway
|