@sap/cds-compiler 2.15.4 → 3.0.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 +33 -1590
- package/bin/cdsc.js +36 -33
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +220 -103
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +60 -20
- package/lib/base/messages.js +65 -24
- package/lib/base/model.js +44 -2
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +87 -9
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/extend.js +59 -11
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +13 -13
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +193 -218
- package/lib/compiler/shared.js +47 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/csn2edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +25 -30
- package/lib/edm/edmUtils.js +10 -24
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20632 -22313
- package/lib/json/from-csn.js +56 -49
- package/lib/json/to-csn.js +10 -8
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +303 -229
- package/lib/language/language.g4 +573 -629
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +1 -1
- package/lib/model/csnUtils.js +170 -283
- package/lib/model/revealInternalProperties.js +28 -8
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +12 -21
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +334 -339
- package/lib/render/toHdbcds.js +19 -15
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +53 -51
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +19 -22
- package/lib/transform/forOdataNew.js +10 -12
- package/lib/transform/localized.js +22 -16
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +3 -3
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
4
|
const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
5
5
|
const transformUtils = require('./transformUtilsNew');
|
|
6
|
-
const {
|
|
7
|
-
cloneCsnNonDict,
|
|
6
|
+
const { cloneCsnNonDict,
|
|
8
7
|
forEachDefinition,
|
|
9
8
|
forEachMemberRecursively,
|
|
10
9
|
applyTransformationsOnNonDictionary,
|
|
@@ -86,10 +85,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
86
85
|
extractValidFromToKeyElement,
|
|
87
86
|
checkAssignment, checkMultipleAssignments,
|
|
88
87
|
recurseElements, setAnnotation, renameAnnotation,
|
|
89
|
-
expandStructsInExpression
|
|
88
|
+
expandStructsInExpression,
|
|
89
|
+
csnUtils,
|
|
90
90
|
} = transformers;
|
|
91
91
|
|
|
92
|
-
const csnUtils = getUtils(csn);
|
|
93
92
|
const {
|
|
94
93
|
getCsnDef,
|
|
95
94
|
getServiceName,
|
|
@@ -98,11 +97,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
98
97
|
inspectRef,
|
|
99
98
|
artifactRef,
|
|
100
99
|
effectiveType,
|
|
101
|
-
getFinalBaseType,
|
|
102
100
|
} = csnUtils;
|
|
103
101
|
|
|
104
102
|
// are we working with structured OData or not
|
|
105
|
-
const structuredOData = options.
|
|
103
|
+
const structuredOData = options.odataFormat === 'structured' && options.odataVersion === 'v4';
|
|
106
104
|
|
|
107
105
|
// collect all declared non-abstract services from the model
|
|
108
106
|
// use the array when there is a need to identify if an artifact is in a service or not
|
|
@@ -115,7 +113,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
115
113
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
116
114
|
enrichUniversalCsn(csn, options);
|
|
117
115
|
|
|
118
|
-
const keepLocalizedViews = isDeprecatedEnabled(options, '
|
|
116
|
+
const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
|
|
119
117
|
|
|
120
118
|
function acceptLocalizedView(_name, parent) {
|
|
121
119
|
csn.definitions[parent].$localized = true;
|
|
@@ -125,7 +123,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
125
123
|
addLocalizationViews(csn, options, acceptLocalizedView);
|
|
126
124
|
|
|
127
125
|
const cleanup = validate.forOdata(csn, {
|
|
128
|
-
message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services,
|
|
126
|
+
message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
|
|
129
127
|
});
|
|
130
128
|
|
|
131
129
|
|
|
@@ -214,16 +212,16 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
214
212
|
|
|
215
213
|
// Annotate artifacts with their DB names if requested.
|
|
216
214
|
// Skip artifacts that have no DB equivalent anyway
|
|
217
|
-
if (options.
|
|
218
|
-
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.
|
|
215
|
+
if (options.sqlMapping && !(def.kind in skipPersNameKinds))
|
|
216
|
+
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
|
|
219
217
|
|
|
220
218
|
forEachMemberRecursively(def, (member, memberName, propertyName) => {
|
|
221
219
|
// Annotate elements, foreign keys, parameters, etc. with their DB names if requested
|
|
222
220
|
// Only these are actually required and don't annotate virtual elements in entities or types
|
|
223
221
|
// as they have no DB representation (although in views)
|
|
224
|
-
if (options.
|
|
222
|
+
if (options.sqlMapping && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
|
|
225
223
|
// If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
|
|
226
|
-
member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.
|
|
224
|
+
member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
|
|
227
225
|
}
|
|
228
226
|
|
|
229
227
|
// Mark fields with @odata.on.insert/update as @Core.Computed
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
|
-
const { setProp } = require('../base/model');
|
|
4
|
+
const { setProp, isDeprecatedEnabled} = require('../base/model');
|
|
5
5
|
const { hasErrors } = require('../base/messages');
|
|
6
|
-
const {
|
|
6
|
+
const { forEachKey } = require('../utils/objectUtils');
|
|
7
7
|
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
8
8
|
const {
|
|
9
|
+
cloneCsnDictionary,
|
|
9
10
|
cloneCsnNonDict,
|
|
11
|
+
applyAnnotationsFromExtensions,
|
|
10
12
|
forEachDefinition,
|
|
11
13
|
forEachGeneric,
|
|
12
14
|
forAllQueries,
|
|
@@ -67,7 +69,8 @@ const _targetFor = Symbol('_targetFor');
|
|
|
67
69
|
* @param {CSN.Options} options
|
|
68
70
|
* @param {boolean} useJoins If true, rewrite the "localized" association to a
|
|
69
71
|
* join in direct convenience views.
|
|
70
|
-
* @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view
|
|
72
|
+
* @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view
|
|
73
|
+
* name and its parent name provided as parameter should be created
|
|
71
74
|
*/
|
|
72
75
|
function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = null) {
|
|
73
76
|
// Don't try to create convenience views with errors.
|
|
@@ -87,7 +90,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
87
90
|
forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
|
|
88
91
|
|
|
89
92
|
// In case that the user tried to annotate `localized.*` artifacts, apply them.
|
|
90
|
-
|
|
93
|
+
applyAnnotationsFromExtensions(csn, {
|
|
91
94
|
overwrite: true,
|
|
92
95
|
filter: (name) => name.startsWith('localized.')
|
|
93
96
|
});
|
|
@@ -147,7 +150,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
147
150
|
else
|
|
148
151
|
view = createLocalizedViewForEntity(art, artName, textElements);
|
|
149
152
|
|
|
150
|
-
copyPersistenceAnnotations(view, art);
|
|
153
|
+
copyPersistenceAnnotations(view, art, options);
|
|
151
154
|
csn.definitions[viewName] = view;
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -667,21 +670,24 @@ function copyLocation(target, source) {
|
|
|
667
670
|
}
|
|
668
671
|
|
|
669
672
|
/**
|
|
670
|
-
* Copy
|
|
671
|
-
* the target. Ignores existing annotations on the
|
|
673
|
+
* Copy @cds.persistence.exists/skip annotations from the source to
|
|
674
|
+
* the target. Ignores existing annotations on the _target_.
|
|
672
675
|
*
|
|
673
676
|
* @param {CSN.Artifact} target
|
|
674
677
|
* @param {CSN.Artifact} source
|
|
678
|
+
* @param {CSN.Options} options
|
|
675
679
|
*/
|
|
676
|
-
function copyPersistenceAnnotations(target, source) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
680
|
+
function copyPersistenceAnnotations(target, source, options) {
|
|
681
|
+
const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
|
|
682
|
+
forEachKey(source, anno => {
|
|
683
|
+
// Note:
|
|
684
|
+
// Because `.exists` is copied to the convenience view, it could
|
|
685
|
+
// lead to some localization views referencing non-existing ones.
|
|
686
|
+
// But that is the contract: User says that it already exists!
|
|
687
|
+
// In v2, `.exists` was never copied.
|
|
688
|
+
if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
|
|
689
|
+
target[anno] = source[anno];
|
|
690
|
+
});
|
|
685
691
|
}
|
|
686
692
|
|
|
687
693
|
/**
|
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
const { isArtifactInSomeService, isArtifactInService } = require('./utils');
|
|
8
8
|
|
|
9
9
|
function expandToFinalBaseType(csn, transformers, csnUtils, services, options, isExternalServiceMember) {
|
|
10
|
-
const isV4 = options.
|
|
10
|
+
const isV4 = options.odataVersion === 'v4';
|
|
11
11
|
forEachDefinition(csn, (def, defName) => {
|
|
12
12
|
// Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
|
|
13
13
|
forEachMemberRecursively(def, (member) => {
|
|
@@ -87,9 +87,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
87
87
|
if (node.kind === 'event') return;
|
|
88
88
|
|
|
89
89
|
// elements have precedence over type
|
|
90
|
-
if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
|
|
90
|
+
if (node.type && (!isBuiltinType(node.type) && isExpandable(node, defName) || node.kind === 'type')) {
|
|
91
91
|
// 1. Get the final type of the node (resolve derived type chain)
|
|
92
|
-
const finalType = csnUtils.
|
|
92
|
+
const finalType = csnUtils.getFinalBaseTypeWithProps(node.type);
|
|
93
93
|
if (finalType) {
|
|
94
94
|
// The type replacement depends on whether 'node' is a definition or a member[element].
|
|
95
95
|
if (node.kind) {
|
|
@@ -101,7 +101,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
101
101
|
// type A: B; -> {...}
|
|
102
102
|
// type B: C; -> { ... }
|
|
103
103
|
// type C { .... };
|
|
104
|
-
if (isBuiltinType(finalType)) {
|
|
104
|
+
if (isBuiltinType(finalType.type)) {
|
|
105
105
|
// use transformUrilsNew::toFinalBaseType for the moment,
|
|
106
106
|
// as it is collects along the chain of types
|
|
107
107
|
// attributes that need to be propagated
|
|
@@ -129,8 +129,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
129
129
|
// type A: B; -> {...}
|
|
130
130
|
// type B: C; -> { ... }
|
|
131
131
|
// type C { .... };
|
|
132
|
-
if (isBuiltinType(finalType)) {
|
|
133
|
-
// use
|
|
132
|
+
if (isBuiltinType(finalType.type)) {
|
|
133
|
+
// use transformUtilsNew::toFinalBaseType for the moment,
|
|
134
134
|
// as it is collects along the chain of types
|
|
135
135
|
// attributes that need to be propagated
|
|
136
136
|
// enum, length, scale, etc.
|
|
@@ -148,8 +148,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
148
148
|
// example in actions: 'action act() return Primitive; type Primitive: array of String;'
|
|
149
149
|
const currService = csnUtils.getServiceName(defName);
|
|
150
150
|
const finalType = csnUtils.getFinalTypeDef(node.type);
|
|
151
|
-
if (finalType.items &&
|
|
152
|
-
(isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.
|
|
151
|
+
if (finalType.items &&
|
|
152
|
+
(isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
|
|
153
153
|
{
|
|
154
154
|
if (!isArtifactInService(node.type, currService) || !isV4) {
|
|
155
155
|
node.items = finalType.items;
|
|
@@ -186,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
186
186
|
function isUserDefinedBuiltinFromTheCurrService(node, defName) {
|
|
187
187
|
// in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
|
|
188
188
|
// a builtin from the service - do not expand to the final base type
|
|
189
|
-
let finalBaseType = csnUtils.
|
|
190
|
-
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.
|
|
189
|
+
let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
|
|
190
|
+
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
|
|
191
191
|
const currService = csnUtils.getServiceName(defName);
|
|
192
192
|
return node.type && !node.type.ref
|
|
193
193
|
&& isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
|
|
@@ -14,7 +14,7 @@ const { copyAnnotations } = require('../../model/csnUtils');
|
|
|
14
14
|
function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
|
|
15
15
|
const { error } = message;
|
|
16
16
|
// are we working with OData proxies or cross-service refs
|
|
17
|
-
const isMultiSchema = options.
|
|
17
|
+
const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.odataXServiceRefs);
|
|
18
18
|
// collect in this variable all the newly exposed types
|
|
19
19
|
const schemas = Object.create(null);
|
|
20
20
|
const exposedTypes = Object.create(null);
|
|
@@ -122,7 +122,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
122
122
|
if (node.elements && node.elements[elemName].$location)
|
|
123
123
|
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
124
124
|
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
125
|
-
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
125
|
+
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
126
126
|
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
|
|
127
127
|
});
|
|
128
128
|
copyAnnotations(typeDef, newType);
|
|
@@ -151,7 +151,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
151
151
|
* 1) If it's an anonymous structured type (items.elements || elements)
|
|
152
152
|
* 2) If it's a named type resolve to the final type definition and
|
|
153
153
|
* check if this is a structured type
|
|
154
|
-
* Returns an object that indicates
|
|
154
|
+
* Returns an object that indicates
|
|
155
155
|
* - wether or not the type needs exposure
|
|
156
156
|
* - the elements dictionary that needs to be cloned
|
|
157
157
|
* - the typeDef, either the resolved type def or the node itself
|
|
@@ -1,38 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
forEachDefinition,
|
|
3
|
-
forEachMemberRecursively,
|
|
4
|
-
} = require('../../model/csnUtils');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Return true if 'artifact' has an association type
|
|
8
|
-
function isAssociation(artifact) {
|
|
9
|
-
return (artifact.type === 'cds.Association' || artifact.type === 'Association');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Return true if 'artifact' has a composition type
|
|
13
|
-
function isComposition(artifact) {
|
|
14
|
-
return (artifact.type === 'cds.Composition' || artifact.type === 'Composition')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function isAssociationOrComposition(artifact) {
|
|
18
|
-
return isAssociation(artifact) || isComposition(artifact);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isManagedAssociation(artifact) {
|
|
22
|
-
return artifact.target !== undefined && artifact.on === undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function forEachManagedAssociation(csn, callback, isExternalServiceMember) {
|
|
26
|
-
|
|
27
|
-
forEachDefinition(csn, (def) => {
|
|
28
|
-
forEachMemberRecursively(def, (element) => {
|
|
29
|
-
if (isAssociationOrComposition(element) && !element.on) {
|
|
30
|
-
callback(element)
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
}, { skipArtifact: isExternalServiceMember });
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
'use strict';
|
|
36
2
|
|
|
37
3
|
/**
|
|
38
4
|
* Return the definition name, without the prefixed service name
|
|
@@ -87,12 +53,9 @@ function isLocalizedArtifactInService(artName, services) {
|
|
|
87
53
|
}
|
|
88
54
|
|
|
89
55
|
module.exports = {
|
|
90
|
-
forEachManagedAssociation,
|
|
91
56
|
defNameWithoutServiceOrContextName,
|
|
92
57
|
getServiceOfArtifact,
|
|
93
58
|
isArtifactInService,
|
|
94
59
|
isArtifactInSomeService,
|
|
95
|
-
isAssociationOrComposition,
|
|
96
60
|
isLocalizedArtifactInService,
|
|
97
|
-
isManagedAssociation,
|
|
98
61
|
}
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const { hasErrors, makeMessageFunction } = require('../base/messages');
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
|
-
const { csnRefs } = require('../model/csnRefs');
|
|
10
9
|
|
|
11
10
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const { cloneCsnNonDict, cloneCsnDictionary, getUtils
|
|
11
|
+
const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
|
|
12
|
+
const { typeParameters, isBuiltinType } = require('../compiler/builtins');
|
|
13
13
|
const { ModelError } = require("../base/error");
|
|
14
14
|
const { forEach } = require('../utils/objectUtils');
|
|
15
15
|
|
|
@@ -20,20 +20,18 @@ const { forEach } = require('../utils/objectUtils');
|
|
|
20
20
|
// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
|
|
21
21
|
function getTransformers(model, options, pathDelimiter = '_') {
|
|
22
22
|
const { error, warning, info } = makeMessageFunction(model, options);
|
|
23
|
+
const csnUtils = getUtils(model);
|
|
23
24
|
const {
|
|
24
25
|
getCsnDef,
|
|
25
|
-
|
|
26
|
+
getFinalBaseTypeWithProps,
|
|
26
27
|
hasAnnotationValue,
|
|
27
28
|
inspectRef,
|
|
28
29
|
isStructured,
|
|
29
|
-
} = getUtils(model);
|
|
30
|
-
|
|
31
|
-
const {
|
|
32
30
|
effectiveType,
|
|
33
|
-
} =
|
|
34
|
-
|
|
31
|
+
} = csnUtils;
|
|
35
32
|
|
|
36
33
|
return {
|
|
34
|
+
csnUtils,
|
|
37
35
|
resolvePath,
|
|
38
36
|
flattenPath,
|
|
39
37
|
addDefaultTypeFacets,
|
|
@@ -45,7 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
45
43
|
copyTypeProperties,
|
|
46
44
|
isAssociationOperand,
|
|
47
45
|
isDollarSelfOrProjectionOperand,
|
|
48
|
-
getFinalBaseType,
|
|
49
46
|
createExposingProjection,
|
|
50
47
|
createAndAddDraftAdminDataProjection,
|
|
51
48
|
createScalarElement,
|
|
@@ -243,21 +240,19 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
243
240
|
// for type of 'x' -> elem.type is an object, not a string -> use directly
|
|
244
241
|
let elemType;
|
|
245
242
|
if (!elem.elements) // structures do not have final base type
|
|
246
|
-
elemType =
|
|
243
|
+
elemType = getFinalBaseTypeWithProps(elem.type);
|
|
247
244
|
|
|
248
245
|
const struct = elemType ? elemType.elements : elem.elements;
|
|
249
246
|
|
|
250
247
|
// Collect all child elements (recursively) into 'result'
|
|
251
248
|
// TODO: Do not report collisions in the generated elements here, but instead
|
|
252
|
-
//
|
|
249
|
+
// leave that work to the receiver of this result
|
|
253
250
|
let result = Object.create(null);
|
|
254
251
|
const addGeneratedFlattenedElement = (e, eName) => {
|
|
255
|
-
if(result[eName])
|
|
256
|
-
error(
|
|
257
|
-
|
|
258
|
-
} else {
|
|
252
|
+
if (result[eName])
|
|
253
|
+
error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
|
|
254
|
+
else
|
|
259
255
|
result[eName] = e;
|
|
260
|
-
}
|
|
261
256
|
}
|
|
262
257
|
forEach(struct, (childName, childElem) => {
|
|
263
258
|
if (isStructured(childElem)) {
|
|
@@ -287,7 +282,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
287
282
|
|
|
288
283
|
// Fix all collected flat elements (names, annotations, properties, origin ..)
|
|
289
284
|
forEach(result, (name, flatElem) => {
|
|
290
|
-
// Copy annotations from struct (not overwriting, because deep annotations should have precedence)
|
|
285
|
+
// Copy annotations from struct (not overwriting, because deep annotations should have precedence).
|
|
286
|
+
// Attention:
|
|
287
|
+
// This has historic reasons. We don't copy doc-comments because copying annotations
|
|
288
|
+
// is questionable to begin with. Only selected annotations should have been copied,
|
|
289
|
+
// if at all.
|
|
291
290
|
copyAnnotations(elem, flatElem, false);
|
|
292
291
|
// Copy selected type properties
|
|
293
292
|
const props = ['key', 'virtual', 'masked', 'viaAll'];
|
|
@@ -395,78 +394,65 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
395
394
|
}
|
|
396
395
|
|
|
397
396
|
/**
|
|
398
|
-
* Replace the type of '
|
|
399
|
-
*
|
|
400
|
-
* Similar with associations and compositions (we probably need a _baseType link)
|
|
397
|
+
* Replace the type of 'nodeWithType' with its final base type, i.e. copy relevant type properties and
|
|
398
|
+
* set the `type` property to the builtin if scalar or delete it if structured/arrayed.
|
|
401
399
|
*
|
|
402
|
-
* @param {
|
|
400
|
+
* @param {object} nodeWithType
|
|
403
401
|
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
404
402
|
* @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
|
|
405
|
-
* @returns {void}
|
|
406
403
|
*/
|
|
407
|
-
function toFinalBaseType(
|
|
408
|
-
|
|
409
|
-
if (!
|
|
410
|
-
// In case of a ref -> Follow the ref
|
|
411
|
-
if (node.type && node.type.ref) {
|
|
412
|
-
const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
|
|
413
|
-
if(finalBaseType === null)
|
|
414
|
-
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
415
|
-
if(finalBaseType.elements) {
|
|
416
|
-
// This changes the order - to be discussed!
|
|
417
|
-
node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
|
|
418
|
-
delete node.type; // delete the type reference as edm processing does not expect it
|
|
419
|
-
} else if(finalBaseType.items) {
|
|
420
|
-
// This changes the order - to be discussed!
|
|
421
|
-
node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
|
|
422
|
-
delete node.type;
|
|
423
|
-
} else {
|
|
424
|
-
node.type=finalBaseType;
|
|
425
|
-
}
|
|
404
|
+
function toFinalBaseType(nodeWithType, resolved = new WeakMap(), keepLocalized = false) {
|
|
405
|
+
const type = nodeWithType?.type;
|
|
406
|
+
if (!type || nodeWithType.elements || nodeWithType.items || resolved.has(nodeWithType)) {
|
|
426
407
|
return;
|
|
427
408
|
}
|
|
428
|
-
//
|
|
429
|
-
|
|
409
|
+
// The caller may use `{ art }` syntax for `{ ref }` objects, but we only use
|
|
410
|
+
// it to indicate that an artifact has been processed.
|
|
411
|
+
resolved.set(nodeWithType, nodeWithType);
|
|
430
412
|
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (typeDef.items || typeDef.elements) {
|
|
435
|
-
// cloneCsn only works correctly if we start "from the top"
|
|
436
|
-
const cloneTypeDef = cloneCsnNonDict(typeDef, options);
|
|
437
|
-
// With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
|
|
438
|
-
if(typeDef.items) {
|
|
439
|
-
delete node.type;
|
|
440
|
-
if(!node.items)
|
|
441
|
-
Object.assign(node, {items: cloneTypeDef.items});
|
|
442
|
-
}
|
|
443
|
-
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
444
|
-
if(!typeDef.items)
|
|
445
|
-
delete node.type;
|
|
446
|
-
if(!node.elements)
|
|
447
|
-
Object.assign(node, {elements: cloneTypeDef.elements});
|
|
448
|
-
}
|
|
413
|
+
// Nothing to copy from builtin.
|
|
414
|
+
if (typeof type === 'string' && isBuiltinType(type))
|
|
415
|
+
return;
|
|
449
416
|
|
|
417
|
+
let typeRef = null;
|
|
418
|
+
if (resolved.has(type)) {
|
|
419
|
+
typeRef = resolved.get(type)?.art
|
|
420
|
+
// The cached entry may not be resolved, yet.
|
|
421
|
+
if (typeRef.type && !isBuiltinType(typeRef.type))
|
|
422
|
+
typeRef = getFinalBaseTypeWithProps(typeRef.type);
|
|
423
|
+
} else {
|
|
424
|
+
typeRef = getFinalBaseTypeWithProps(type);
|
|
425
|
+
}
|
|
450
426
|
|
|
427
|
+
if (typeRef.elements || typeRef.items) {
|
|
428
|
+
// Copy elements/items and we're finished. No need to look up actual base type,
|
|
429
|
+
// since it must also be structured and must contain at least as many elements,
|
|
430
|
+
// if not more (in client style CSN).
|
|
431
|
+
if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
432
|
+
nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
|
|
433
|
+
delete nodeWithType.type;
|
|
434
|
+
}
|
|
435
|
+
if (typeRef.items) {
|
|
436
|
+
nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
|
|
437
|
+
delete nodeWithType.type;
|
|
438
|
+
}
|
|
451
439
|
return;
|
|
452
440
|
}
|
|
453
|
-
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
|
|
441
|
+
|
|
442
|
+
if (typeRef.enum && nodeWithType.enum === undefined)
|
|
443
|
+
nodeWithType.enum = cloneCsnDictionary(typeRef.enum, options);
|
|
444
|
+
|
|
445
|
+
// Copy type and type arguments (+ localized)
|
|
446
|
+
|
|
447
|
+
for (const param of typeParameters.list) {
|
|
448
|
+
if (nodeWithType[param] === undefined && typeRef[param] !== undefined &&!typeRef.$default) {
|
|
449
|
+
nodeWithType[param] = typeRef[param];
|
|
450
|
+
}
|
|
457
451
|
}
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
if (
|
|
461
|
-
|
|
462
|
-
if (node.scale === undefined && typeDef.scale !== undefined)
|
|
463
|
-
Object.assign(node, { scale: typeDef.scale });
|
|
464
|
-
if (node.srid === undefined && typeDef.srid !== undefined)
|
|
465
|
-
Object.assign(node, { srid: typeDef.srid });
|
|
466
|
-
if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
|
|
467
|
-
Object.assign(node, { localized: typeDef.localized });
|
|
468
|
-
node.type = typeDef.type;
|
|
469
|
-
toFinalBaseType(node);
|
|
452
|
+
if (keepLocalized && nodeWithType.localized === undefined && typeRef.localized !== undefined)
|
|
453
|
+
nodeWithType.localized = typeRef.localized;
|
|
454
|
+
if (typeRef.type)
|
|
455
|
+
nodeWithType.type = typeRef.type;
|
|
470
456
|
}
|
|
471
457
|
|
|
472
458
|
// Return a full projection 'projectionId' of artifact 'art' for exposure in 'service'.
|
|
@@ -57,10 +57,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
57
57
|
const options = model.options || inputOptions;
|
|
58
58
|
|
|
59
59
|
// create JOINs for foreign key paths
|
|
60
|
-
const noJoinForFK = options.forHana ? !options.
|
|
60
|
+
const noJoinForFK = options.forHana ? !options.joinfk : true;
|
|
61
61
|
|
|
62
62
|
// Note: This is called from the 'forHana' transformations, so it is controlled by its options)
|
|
63
|
-
const pathDelimiter = (options.forHana && options.
|
|
63
|
+
const pathDelimiter = (options.forHana && options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
64
64
|
|
|
65
65
|
forEachDefinition(model, prepareAssociations);
|
|
66
66
|
forEachDefinition(model, transformQueries);
|
|
@@ -12,7 +12,7 @@ const { setAnnotationIfNotDefined } = require('./utils');
|
|
|
12
12
|
*/
|
|
13
13
|
function setCoreComputedOnViews(csn) {
|
|
14
14
|
const {
|
|
15
|
-
artifactRef, getColumn, getElement,
|
|
15
|
+
artifactRef, getColumn, getElement, getOrigin,
|
|
16
16
|
} = getUtils(csn, 'init-all');
|
|
17
17
|
|
|
18
18
|
forEachDefinition(csn, (artifact) => {
|
|
@@ -57,7 +57,15 @@ function setCoreComputedOnViews(csn) {
|
|
|
57
57
|
const column = getColumn(element);
|
|
58
58
|
if (column)
|
|
59
59
|
return column;
|
|
60
|
-
|
|
60
|
+
const from = getElementFromFrom(name, base.from);
|
|
61
|
+
if (from)
|
|
62
|
+
return from;
|
|
63
|
+
// For .expand/.inline, we can find it via origin
|
|
64
|
+
// Although I would have expected to find it via getColumn...
|
|
65
|
+
const origin = getOrigin(element);
|
|
66
|
+
if (origin)
|
|
67
|
+
return origin;
|
|
68
|
+
throw new Error(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/**
|
|
@@ -87,10 +95,7 @@ function setCoreComputedOnViews(csn) {
|
|
|
87
95
|
return getElementFromFrom(name, base.SET.args[0]);
|
|
88
96
|
}
|
|
89
97
|
else if (base.args && base.join) {
|
|
90
|
-
|
|
91
|
-
if (!result)
|
|
92
|
-
throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
|
|
93
|
-
return result;
|
|
98
|
+
return checkJoinSources(base.args, name);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
throw new Error(JSON.stringify(base));
|
|
@@ -29,10 +29,7 @@ module.exports = (csn, options) => {
|
|
|
29
29
|
const definitionPropagationRules = {
|
|
30
30
|
'@cds.autoexpose': onlyViaArtifact,
|
|
31
31
|
'@fiori.draft.enabled': onlyViaArtifact,
|
|
32
|
-
'@':
|
|
33
|
-
if (source[prop] !== null)
|
|
34
|
-
target[prop] = source[prop];
|
|
35
|
-
},
|
|
32
|
+
'@': nullStopsPropagation,
|
|
36
33
|
// Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
|
|
37
34
|
elements: onlyTypeDef,
|
|
38
35
|
'@cds.persistence.exists': skip,
|
|
@@ -48,7 +45,7 @@ module.exports = (csn, options) => {
|
|
|
48
45
|
'@cds.autoexposed': skip,
|
|
49
46
|
'@cds.redirection.target': skip,
|
|
50
47
|
type: always,
|
|
51
|
-
doc:
|
|
48
|
+
doc: nullStopsPropagation,
|
|
52
49
|
length: always,
|
|
53
50
|
precision: always,
|
|
54
51
|
scale: always,
|
|
@@ -94,6 +91,10 @@ module.exports = (csn, options) => {
|
|
|
94
91
|
}, // overwrite from defProps
|
|
95
92
|
kind: skip,
|
|
96
93
|
val: always,
|
|
94
|
+
type: notWithItemsOrElements,
|
|
95
|
+
target: notWithItemsOrElements,
|
|
96
|
+
keys: notWithItemsOrElements,
|
|
97
|
+
cardinality: notWithItemsOrElements,
|
|
97
98
|
};
|
|
98
99
|
|
|
99
100
|
generate();
|
|
@@ -702,6 +703,19 @@ function notWithTypeOrigin(prop, target, source) {
|
|
|
702
703
|
target[prop] = source[prop];
|
|
703
704
|
}
|
|
704
705
|
|
|
706
|
+
/**
|
|
707
|
+
* The value `null` tells us to skip the propagation of the property.
|
|
708
|
+
* This is the case e.g. for `doc` or for annotations.
|
|
709
|
+
*
|
|
710
|
+
* @param {string} prop
|
|
711
|
+
* @param {CSN.Element} target
|
|
712
|
+
* @param {CSN.Element} source
|
|
713
|
+
*/
|
|
714
|
+
function nullStopsPropagation(prop, target, source) {
|
|
715
|
+
if (source[prop] !== null)
|
|
716
|
+
target[prop] = source[prop];
|
|
717
|
+
}
|
|
718
|
+
|
|
705
719
|
/**
|
|
706
720
|
* Special propagation rules for .items - depending on the exact type of .items and the
|
|
707
721
|
* way it was referenced (type of, direct type, direct many), we need to propagate (or not).
|
|
@@ -722,6 +736,20 @@ function specialItemsRules(prop, target, source) {
|
|
|
722
736
|
target[prop] = source[prop];
|
|
723
737
|
}
|
|
724
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Don't propagate certain properties if the target already has a .items or .elements
|
|
741
|
+
*
|
|
742
|
+
* This happens with .expand/.inline
|
|
743
|
+
*
|
|
744
|
+
* @param {string} prop
|
|
745
|
+
* @param {CSN.Element} target
|
|
746
|
+
* @param {CSN.Element} source
|
|
747
|
+
*/
|
|
748
|
+
function notWithItemsOrElements(prop, target, source) {
|
|
749
|
+
if (!target.items && !target.elements || !source.target)
|
|
750
|
+
target[prop] = source[prop];
|
|
751
|
+
}
|
|
752
|
+
|
|
725
753
|
/**
|
|
726
754
|
* Some properties must not be copied over if the type of this member
|
|
727
755
|
* is a reference to another element.
|