@sap/cds-compiler 6.4.2 → 6.5.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 +87 -1159
- package/README.md +1 -10
- package/doc/IncompatibleChanges_v5.md +436 -0
- package/doc/IncompatibleChanges_v6.md +659 -0
- package/doc/Versioning.md +3 -7
- package/lib/api/main.js +1 -0
- package/lib/api/options.js +5 -0
- package/lib/api/validate.js +3 -0
- package/lib/base/message-registry.js +25 -2
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +3 -2
- package/lib/checks/actionsFunctions.js +6 -3
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
- package/lib/checks/existsInForbiddenPlaces.js +32 -0
- package/lib/checks/existsMustEndInAssoc.js +1 -1
- package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
- package/lib/checks/validator.js +6 -2
- package/lib/compiler/assert-consistency.js +5 -7
- package/lib/compiler/builtins.js +5 -6
- package/lib/compiler/checks.js +4 -8
- package/lib/compiler/define.js +244 -459
- package/lib/compiler/extend.js +297 -11
- package/lib/compiler/finalize-parse-cdl.js +2 -10
- package/lib/compiler/generate.js +29 -1
- package/lib/compiler/populate.js +21 -63
- package/lib/compiler/propagator.js +1 -2
- package/lib/compiler/resolve.js +2 -12
- package/lib/compiler/shared.js +145 -114
- package/lib/compiler/tweak-assocs.js +14 -10
- package/lib/compiler/utils.js +97 -0
- package/lib/compiler/xpr-rewrite.js +113 -140
- package/lib/edm/annotations/edmJson.js +9 -6
- package/lib/edm/annotations/genericTranslation.js +8 -4
- package/lib/edm/csn2edm.js +3 -4
- package/lib/edm/edmInboundChecks.js +1 -2
- package/lib/edm/edmPreprocessor.js +3 -3
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +4 -3
- package/lib/gen/Dictionary.json +16 -1
- package/lib/json/from-csn.js +4 -6
- package/lib/json/to-csn.js +3 -3
- package/lib/model/csnRefs.js +13 -4
- package/lib/model/enrichCsn.js +4 -2
- package/lib/optionProcessor.js +8 -4
- package/lib/parsers/AstBuildingParser.js +1 -1
- package/lib/render/utils/sql.js +3 -2
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +3 -3
- package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
- package/lib/transform/db/assocsToQueries/transformExists.js +17 -13
- package/lib/transform/db/assocsToQueries/utils.js +1 -6
- package/lib/transform/db/backlinks.js +4 -4
- package/lib/transform/db/cdsPersistence.js +4 -4
- package/lib/transform/db/constraints.js +4 -4
- package/lib/transform/db/expansion.js +5 -5
- package/lib/transform/db/flattening.js +4 -5
- package/lib/transform/db/rewriteCalculatedElements.js +3 -3
- package/lib/transform/db/temporal.js +11 -11
- package/lib/transform/draft/db.js +2 -0
- package/lib/transform/draft/odata.js +5 -7
- package/lib/transform/effective/flattening.js +1 -2
- package/lib/transform/forOdata.js +3 -3
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +13 -20
- package/lib/transform/odata/createForeignKeys.js +1 -2
- package/lib/transform/odata/flattening.js +1 -2
- package/lib/transform/odata/toFinalBaseType.js +52 -55
- package/lib/transform/transformUtils.js +3 -4
- package/package.json +3 -3
- package/doc/CHANGELOG_BETA.md +0 -464
- package/doc/CHANGELOG_DEPRECATED.md +0 -235
|
@@ -124,7 +124,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
* Turn .expand/.inline into normal refs.
|
|
127
|
+
* Turn .expand/.inline into normal refs. `@cds.persistence.skip` .expand with to-many (and all transitive views).
|
|
128
128
|
* For such skipped things, error for usage of assoc pointing to them and ignore publishing of assoc pointing to them.
|
|
129
129
|
*/
|
|
130
130
|
function rewriteExpandInline() {
|
|
@@ -269,7 +269,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
271
|
* Mark the given artifact and all (transitively) dependent artifacts as `toDummify`.
|
|
272
|
-
* This means that they will be replaced with simple dummy views in
|
|
272
|
+
* This means that they will be replaced with simple dummy views in `@dummify`
|
|
273
273
|
*
|
|
274
274
|
* @param {CSN.Artifact} artifact
|
|
275
275
|
* @param {string} name
|
|
@@ -544,7 +544,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
/**
|
|
547
|
-
* Create a simple dummy view marked with
|
|
547
|
+
* Create a simple dummy view marked with `@cds.persistence.skip`
|
|
548
548
|
*
|
|
549
549
|
* @param {string} source
|
|
550
550
|
* @returns {CSN.Artifact}
|
|
@@ -646,14 +646,14 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
646
646
|
* leafs `a_b` and `a_c`, if `art` has elements `b` and `c`.
|
|
647
647
|
* @param {string[]} colTypeRef
|
|
648
648
|
* Expanded type for the column. Basically the path to the to-be-expanded `art`.
|
|
649
|
-
* @param {(currentRef:
|
|
649
|
+
* @param {(currentRef: CSN.Ref, currentAlias: string[]) => object} leafCallback
|
|
650
650
|
* Callback when leaf nodes are reached. currentRef is the type reference for the expanded
|
|
651
651
|
* column. currentAlias is the columns calculated alias.
|
|
652
652
|
* @returns {object[]}
|
|
653
653
|
*/
|
|
654
654
|
function _expandStructCol( art, colName, colTypeRef, leafCallback ) {
|
|
655
655
|
const expanded = [];
|
|
656
|
-
/** @type {Array<[CSN.Element,
|
|
656
|
+
/** @type {Array<[CSN.Element, CSN.Column, string[]]>} */
|
|
657
657
|
const stack = [ [ art, colTypeRef, [ colName ] ] ];
|
|
658
658
|
while (stack.length > 0) {
|
|
659
659
|
const [ current, currentRef, currentAlias ] = stack.pop();
|
|
@@ -468,9 +468,9 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFun
|
|
|
468
468
|
*
|
|
469
469
|
* If a structure contains an assoc, this will also be resolved and vice versa
|
|
470
470
|
*
|
|
471
|
-
* @param {
|
|
472
|
-
* @param {
|
|
473
|
-
* @param {
|
|
471
|
+
* @param {CSN.Association} assoc
|
|
472
|
+
* @param {string} assocName
|
|
473
|
+
* @param {CSN.Path} path
|
|
474
474
|
*/
|
|
475
475
|
function flattenFKs( assoc, assocName, path ) {
|
|
476
476
|
if (!assoc.keys)
|
|
@@ -697,7 +697,6 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFun
|
|
|
697
697
|
* @returns {Array[]} First element of every sub-array is the foreign key name, second is the foreign key definition
|
|
698
698
|
*/
|
|
699
699
|
function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathDelimiter, lvl = 0, originalKey = { }) {
|
|
700
|
-
const special$self = !csn?.definitions?.$self && '$self';
|
|
701
700
|
const isInspectRefResult = !Array.isArray(path);
|
|
702
701
|
|
|
703
702
|
let fks = [];
|
|
@@ -707,7 +706,7 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
707
706
|
let finalElement = element;
|
|
708
707
|
let finalTypeName; // TODO: Find a way to not rely on $path?
|
|
709
708
|
// TODO: effectiveType's return value is 'path' for the next inspectRef
|
|
710
|
-
if (element.type && !isBuiltinType(element.type)
|
|
709
|
+
if (element.type && !isBuiltinType(element.type)) {
|
|
711
710
|
const tmpElt = csnUtils.effectiveType(element);
|
|
712
711
|
// effective type resolves to structs and enums only but not scalars
|
|
713
712
|
if (Object.keys(tmpElt).length) {
|
|
@@ -141,9 +141,9 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
141
141
|
/**
|
|
142
142
|
* @param {object} parent
|
|
143
143
|
* @param {string} prop
|
|
144
|
-
* @param ref
|
|
145
|
-
* @param p
|
|
146
|
-
* @param root
|
|
144
|
+
* @param {CSN.Ref} ref
|
|
145
|
+
* @param {CSN.Path} p
|
|
146
|
+
* @param {CSN.Element} root
|
|
147
147
|
*/
|
|
148
148
|
function transformRef(parent, prop, ref, p, root) {
|
|
149
149
|
const {
|
|
@@ -11,8 +11,8 @@ const validToString = '@cds.valid.to';
|
|
|
11
11
|
const validFromString = '@cds.valid.from';
|
|
12
12
|
/**
|
|
13
13
|
* Get the forEachDefinition callback function that adds a where condition to views that
|
|
14
|
-
* - are annotated with
|
|
15
|
-
* - have only one
|
|
14
|
+
* - are annotated with `@cds.valid.from` and `@cds.valid.to`,
|
|
15
|
+
* - have only one `@cds.valid.from` and `@cds.valid.to`,
|
|
16
16
|
* - and both annotations come from the same entity
|
|
17
17
|
*
|
|
18
18
|
* If the view has one of the annotations but the other conditions are not met, an error will be raised.
|
|
@@ -30,8 +30,8 @@ function getViewDecorator( csn, messageFunctions, csnUtils, options ) {
|
|
|
30
30
|
return addTemporalWhereConditionToView;
|
|
31
31
|
/**
|
|
32
32
|
* Add a where condition to views that
|
|
33
|
-
* - are annotated with
|
|
34
|
-
* - have only one
|
|
33
|
+
* - are annotated with `@cds.valid.from` and `@cds.valid.to`,
|
|
34
|
+
* - have only one `@cds.valid.from` and `@cds.valid.to`,
|
|
35
35
|
* - and both annotations come from the same entity
|
|
36
36
|
*
|
|
37
37
|
* If the view has one of the annotations but the other conditions are not met, an error will be raised.
|
|
@@ -97,10 +97,10 @@ function getViewDecorator( csn, messageFunctions, csnUtils, options ) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Get all elements tagged with
|
|
100
|
+
* Get all elements tagged with `@cds.valid.from/to` from the union of all entities of the from-clause.
|
|
101
101
|
*
|
|
102
|
-
* @param {
|
|
103
|
-
* @returns {Array[]} Array where first field is array of elements with
|
|
102
|
+
* @param {CSN.QueryFrom} combined union of all entities of the from-clause
|
|
103
|
+
* @returns {Array[]} Array where first field is array of elements with `@cds.valid.from`, second field is array of elements with `@cds.valid.to`.
|
|
104
104
|
*/
|
|
105
105
|
function getFromToElements( combined ) {
|
|
106
106
|
const from = [];
|
|
@@ -121,7 +121,7 @@ function getViewDecorator( csn, messageFunctions, csnUtils, options ) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
* Check if the given SELECT has a falsy
|
|
124
|
+
* Check if the given SELECT has a falsy `@cds.valid.from` and a falsy `@cds.valid.to`
|
|
125
125
|
*
|
|
126
126
|
* @param {CSN.QuerySelect} SELECT
|
|
127
127
|
* @param {CSN.Elements} elements
|
|
@@ -150,13 +150,13 @@ function getViewDecorator( csn, messageFunctions, csnUtils, options ) {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
* Get the forEachDefinition callback function that collects all usages of
|
|
153
|
+
* Get the forEachDefinition callback function that collects all usages of `@cds.valid.from/to/key` and checks that
|
|
154
154
|
* - the assignment is on a valid element
|
|
155
155
|
* - the annotation is only assigned once
|
|
156
156
|
* - key is only used in conjunction with from and to
|
|
157
157
|
*
|
|
158
|
-
* Furthermore,
|
|
159
|
-
* If
|
|
158
|
+
* Furthermore, `@cds.valid.from` and `@cds.valid.key` is processed - `@cds.valid.from` is marked as key or marked as unique if `@cds.valid.key` is used.
|
|
159
|
+
* If `@cds.valid.key` is used, the real key-elements have their key-property removed (set non-enumerable as $key) and instead the `@cds.valid.key`-marked elements have it added.
|
|
160
160
|
*
|
|
161
161
|
* @param {CSN.Model} csn
|
|
162
162
|
* @param {CSN.Options} options
|
|
@@ -188,6 +188,8 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
188
188
|
// Copy all elements
|
|
189
189
|
for (const elemName in artifact.elements) {
|
|
190
190
|
const origElem = artifact.elements[elemName];
|
|
191
|
+
// elements with $calc are rendered as usual
|
|
192
|
+
|
|
191
193
|
if (origElem.value?.stored) {
|
|
192
194
|
calcOnWriteElements.push(elemName);
|
|
193
195
|
}
|
|
@@ -27,11 +27,12 @@ const { makeMessageFunction } = require('../../base/messages');
|
|
|
27
27
|
* @param {CSN.Options} options
|
|
28
28
|
* @param {string[]|undefined} services Will be calculated JIT if not provided
|
|
29
29
|
* @param {object} [messageFunctions]
|
|
30
|
+
* @param {function} [isExternalServiceMember]
|
|
30
31
|
* @returns {CSN.Model} Returns the transformed input model
|
|
31
32
|
* @todo should be done by the compiler - Check associations for valid foreign keys
|
|
32
33
|
* @todo check if needed at all: Remove '$projection' from paths in the element's ON-condition
|
|
33
34
|
*/
|
|
34
|
-
function generateDrafts( csn, options, services, messageFunctions ) {
|
|
35
|
+
function generateDrafts( csn, options, services, messageFunctions, isExternalServiceMember ) {
|
|
35
36
|
// TEMP(2024-02-26): Temporary! Umbrella uses this file directly in cds/lib/compile/for/drafts.js#L1
|
|
36
37
|
messageFunctions ??= makeMessageFunction(csn, options, 'odata-drafts');
|
|
37
38
|
|
|
@@ -53,14 +54,11 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
53
54
|
services = getServiceNames(csn);
|
|
54
55
|
|
|
55
56
|
const visitedArtifacts = Object.create(null);
|
|
56
|
-
|
|
57
|
-
const externalServices = services.filter(serviceName => csn.definitions[serviceName]['@cds.external']);
|
|
58
|
-
// @ts-ignore
|
|
59
|
-
const isExternalServiceMember = (_art, name) => externalServices.includes(getServiceName(name));
|
|
57
|
+
|
|
60
58
|
const filterDict = Object.create(null);
|
|
61
59
|
|
|
62
60
|
// validate the 'DRAFT.DraftAdministrativeData_DraftMessage' type if already present in the model
|
|
63
|
-
if (options.draftMessages) {
|
|
61
|
+
if (options.draftMessages && options.odataVersion === 'v4') {
|
|
64
62
|
const draftAdminDataMessagesType = csn.definitions['DRAFT.DraftAdministrativeData_DraftMessage'];
|
|
65
63
|
if (draftAdminDataMessagesType && !isValidDraftAdminDataMessagesType(draftAdminDataMessagesType)) {
|
|
66
64
|
error(null, [ 'definitions', 'DRAFT.DraftAdministrativeData_DraftMessage' ], { name: 'DRAFT.DraftAdministrativeData_DraftMessage' },
|
|
@@ -181,7 +179,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
181
179
|
// ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
|
|
182
180
|
siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
|
|
183
181
|
|
|
184
|
-
if (options.draftMessages) {
|
|
182
|
+
if (options.draftMessages && options.odataVersion === 'v4') {
|
|
185
183
|
const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
|
|
186
184
|
addElement(draftMessages, artifact, artifactName);
|
|
187
185
|
|
|
@@ -62,13 +62,12 @@ function flattenRefs(csn, options, csnUtils, messageFunctions) {
|
|
|
62
62
|
|
|
63
63
|
// explicit binding parameter of bound action
|
|
64
64
|
if (def.actions) {
|
|
65
|
-
const special$self = !csn?.definitions?.$self && '$self';
|
|
66
65
|
Object.entries(def.actions).forEach(([ an, a ]) => {
|
|
67
66
|
if (a.params) {
|
|
68
67
|
const params = Object.entries(a.params);
|
|
69
68
|
const firstParam = params[0][1];
|
|
70
69
|
const type = firstParam?.items?.type || firstParam?.type;
|
|
71
|
-
if (type ===
|
|
70
|
+
if (type === '$self') {
|
|
72
71
|
const bindingParamName = params[0][0];
|
|
73
72
|
const markBindingParam = {
|
|
74
73
|
ref: (parent, prop, xpr) => {
|
|
@@ -117,9 +117,9 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
117
117
|
// use the array when there is a need to identify if an artifact is in a service or not
|
|
118
118
|
const services = getServiceNames(csn);
|
|
119
119
|
// @ts-ignore
|
|
120
|
-
const externalServices = services.filter(serviceName => csn.definitions[serviceName]['@cds.external']);
|
|
120
|
+
const externalServices = services.filter(serviceName => csn.definitions[serviceName]['@cds.external'] && csn.definitions[serviceName]['@cds.external'] !== 2);
|
|
121
121
|
// @ts-ignore
|
|
122
|
-
const isExternalServiceMember = (art, name) => !!(externalServices.includes(getServiceName(name)) || (art && art['@cds.external']));
|
|
122
|
+
const isExternalServiceMember = (art, name) => !!(externalServices.includes(getServiceName(name)) || (art && art['@cds.external'] && art['@cds.external'] !== 2));
|
|
123
123
|
|
|
124
124
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
125
125
|
enrichUniversalCsn(csn, options);
|
|
@@ -127,7 +127,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
127
127
|
// - Generate artificial draft fields on a structured CSN if requested, flattening and struct
|
|
128
128
|
// expansion do their magic including foreign key generation and annotation propagation.
|
|
129
129
|
// Tenantenizer has to decorate the DraftAdministrativeData, so draft decoration must be done before.
|
|
130
|
-
generateDrafts(csn, options, services, messageFunctions);
|
|
130
|
+
generateDrafts(csn, options, services, messageFunctions, isExternalServiceMember);
|
|
131
131
|
|
|
132
132
|
if (options.tenantDiscriminator)
|
|
133
133
|
addTenantFields(csn, options);
|
|
@@ -49,7 +49,7 @@ function forEachDefinition(csn, cb) {
|
|
|
49
49
|
* such as flattening, wildcard expansion, etc.
|
|
50
50
|
*
|
|
51
51
|
* @param {CSN.Model} csn
|
|
52
|
-
* @param {
|
|
52
|
+
* @param {SqlOptions} options
|
|
53
53
|
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
54
54
|
*/
|
|
55
55
|
function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
@@ -58,23 +58,7 @@ const annoPersistenceSkip = '@cds.persistence.skip';
|
|
|
58
58
|
* @param {CSN.Model} csn
|
|
59
59
|
* Input CSN model. Should not have existing convenience views.
|
|
60
60
|
*
|
|
61
|
-
* @param {
|
|
62
|
-
* CSN options. Only few options are used, see below for important ones.
|
|
63
|
-
* Options such as `testMode` or `testSortCsn` can also be set.
|
|
64
|
-
*
|
|
65
|
-
* @param {string} [options.localizedLanguageFallback]
|
|
66
|
-
* Valid values (if set): 'none', 'coalesce' (default)
|
|
67
|
-
* Whether to use a `coalesce()` function when selecting from `.texts` entities.
|
|
68
|
-
* If not set, untranslated strings may not return any value. If 'coalesce'
|
|
69
|
-
* is used, it will fall back to the original string.
|
|
70
|
-
*
|
|
71
|
-
* @param {boolean} [options.localizedWithoutCoalesce]
|
|
72
|
-
* Deprecated version of localizedLanguageFallback. Do not use.
|
|
73
|
-
*
|
|
74
|
-
* @param {boolean} [options.fewerLocalizedViews]
|
|
75
|
-
* Default: true
|
|
76
|
-
*
|
|
77
|
-
* @param {boolean} [options.testMode]
|
|
61
|
+
* @param {CSN.Options} options
|
|
78
62
|
*
|
|
79
63
|
* @param {object} config
|
|
80
64
|
* Configuration for creating convenience views. Non-user visible options.
|
|
@@ -252,12 +236,14 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
252
236
|
}
|
|
253
237
|
|
|
254
238
|
function createJoinConditionFromLocaleElement() {
|
|
239
|
+
const targetAlias = 'localized_1';
|
|
240
|
+
const sourceAlias = 'L_0';
|
|
255
241
|
return adaptExpr(entity.elements.localized.on);
|
|
256
242
|
|
|
257
243
|
function adaptExpr(expr) {
|
|
258
244
|
// We only support a few specific ON-conditions, not generic expressions.
|
|
259
245
|
// In case of unsupported ON-conditions, we emit an error.
|
|
260
|
-
|
|
246
|
+
const res = expr.map((x) => {
|
|
261
247
|
if (!x || typeof x === 'string')
|
|
262
248
|
return x;
|
|
263
249
|
if (x.xpr)
|
|
@@ -273,14 +259,21 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
273
259
|
);
|
|
274
260
|
return x;
|
|
275
261
|
});
|
|
262
|
+
|
|
263
|
+
// the `localized` association does not contain the `tenant` element, so we need to add it here
|
|
264
|
+
const addTenantCol = options.tenantDiscriminator && entity.elements.tenant?.key;
|
|
265
|
+
if (addTenantCol)
|
|
266
|
+
return [ { ref: [ targetAlias, 'tenant' ] }, '=', { ref: [ sourceAlias, 'tenant' ] }, 'AND', { xpr: [ ...res ] } ];
|
|
267
|
+
|
|
268
|
+
return res;
|
|
276
269
|
}
|
|
277
270
|
|
|
278
271
|
function adaptRef(expr) {
|
|
279
272
|
if (expr.ref[0].charAt(0) === '$') // variable
|
|
280
273
|
return { ref: [ ...expr.ref ] };
|
|
281
274
|
if (expr.ref[0] === 'localized') // target side
|
|
282
|
-
return { ref: [
|
|
283
|
-
return { ref: [
|
|
275
|
+
return { ref: [ targetAlias, ...expr.ref.slice(1) ] };
|
|
276
|
+
return { ref: [ sourceAlias, ...expr.ref ] }; // source side
|
|
284
277
|
}
|
|
285
278
|
}
|
|
286
279
|
}
|
|
@@ -76,7 +76,6 @@ function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iter
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function createForeignKeysForElement(path, element, prefix, csn, options, pathDelimiter, lvl = 0, originalKey = {} ) {
|
|
79
|
-
const special$self = !csn?.definitions?.$self && '$self';
|
|
80
79
|
const isInspectRefResult = !Array.isArray(path);
|
|
81
80
|
|
|
82
81
|
let fks = [];
|
|
@@ -87,7 +86,7 @@ function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iter
|
|
|
87
86
|
let finalTypeName;
|
|
88
87
|
|
|
89
88
|
// resolve derived type
|
|
90
|
-
if (element.type && !isBuiltinType(element.type)
|
|
89
|
+
if (element.type && !isBuiltinType(element.type)) {
|
|
91
90
|
const tmpElt = csnUtils.effectiveType(element);
|
|
92
91
|
// effective type resolves to structs and enums only but not scalars
|
|
93
92
|
if (Object.keys(tmpElt).length) {
|
|
@@ -102,13 +102,12 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, getFinalTy
|
|
|
102
102
|
setProp(def, '$flatAnnotations', flatAnnos);
|
|
103
103
|
// explicit binding parameter of bound action
|
|
104
104
|
if (def.actions) {
|
|
105
|
-
const special$self = !csn?.definitions?.$self && '$self';
|
|
106
105
|
Object.entries(def.actions).forEach(([ actionName, action ]) => {
|
|
107
106
|
if (action.params) {
|
|
108
107
|
const params = Object.entries(action.params);
|
|
109
108
|
const firstParam = params[0][1];
|
|
110
109
|
const type = firstParam?.items?.type || firstParam?.type;
|
|
111
|
-
if (type ===
|
|
110
|
+
if (type === '$self') {
|
|
112
111
|
const bindingParamName = params[0][0];
|
|
113
112
|
const markBindingParam = {
|
|
114
113
|
ref: (parent, prop, xpr) => {
|
|
@@ -13,7 +13,6 @@ const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
13
13
|
|
|
14
14
|
function expandToFinalBaseType(csn, transformers, csnUtils, services, options, error) {
|
|
15
15
|
const isV4 = options.odataVersion === 'v4';
|
|
16
|
-
const special$self = !csn?.definitions?.$self && '$self';
|
|
17
16
|
forEachDefinition(csn, (def, defName) => {
|
|
18
17
|
// Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
|
|
19
18
|
forEachMemberRecursively(def, (member, _memberName) => {
|
|
@@ -112,65 +111,63 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
|
|
|
112
111
|
else {
|
|
113
112
|
if (isExpandable(finalBaseType) || node.kind === 'type') {
|
|
114
113
|
// 1. Get the final type of the node (resolve derived type chain)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
type X: Integer;
|
|
114
|
+
// The type replacement depends on whether 'node' is a definition or a member[element].
|
|
115
|
+
if (node.kind) {
|
|
116
|
+
/*
|
|
117
|
+
It is a definition and we expand to builtin type and to elements
|
|
118
|
+
type T: S; --> Integer;
|
|
119
|
+
type S: X; --> Integer;
|
|
120
|
+
type X: Integer;
|
|
123
121
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
type A: B; -> {...}
|
|
123
|
+
type B: C; -> { ... }
|
|
124
|
+
type C { .... };
|
|
125
|
+
*/
|
|
126
|
+
if (isBuiltinType(finalBaseType.type)) {
|
|
127
|
+
/*
|
|
128
|
+
use transformUtils::toFinalBaseType for the moment,
|
|
129
|
+
as it is collects along the chain of types
|
|
130
|
+
attributes that need to be propagated
|
|
131
|
+
enum, length, scale, etc.
|
|
127
132
|
*/
|
|
128
|
-
|
|
129
|
-
/*
|
|
130
|
-
use transformUtils::toFinalBaseType for the moment,
|
|
131
|
-
as it is collects along the chain of types
|
|
132
|
-
attributes that need to be propagated
|
|
133
|
-
enum, length, scale, etc.
|
|
134
|
-
*/
|
|
135
|
-
transformers.toFinalBaseType(node);
|
|
136
|
-
}
|
|
137
|
-
else if (csnUtils.isStructured(finalBaseType)) {
|
|
138
|
-
cloneElements(node, finalBaseType);
|
|
139
|
-
}
|
|
140
|
-
else if (node.type && node.items) {
|
|
141
|
-
delete node.type;
|
|
142
|
-
}
|
|
133
|
+
transformers.toFinalBaseType(node);
|
|
143
134
|
}
|
|
144
|
-
else {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
135
|
+
else if (csnUtils.isStructured(finalBaseType)) {
|
|
136
|
+
cloneElements(node, finalBaseType);
|
|
137
|
+
}
|
|
138
|
+
else if (node.type && node.items) {
|
|
139
|
+
delete node.type;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
/*
|
|
144
|
+
this is a member and we expand to final base only if builtin
|
|
145
|
+
type T: S; --> Integer;
|
|
146
|
+
type S: X; --> Integer;
|
|
147
|
+
type X: Integer;
|
|
150
148
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
149
|
+
type {
|
|
150
|
+
struct_elt: many A; ---> stays the same
|
|
151
|
+
scalar_elt: T; ---> Integer;
|
|
152
|
+
type_ref_elt: type of struct_elt;
|
|
153
|
+
};
|
|
154
|
+
type A: B; -> {...}
|
|
155
|
+
type B: C; -> { ... }
|
|
156
|
+
type C { .... };
|
|
157
|
+
*/
|
|
158
|
+
// eslint-disable-next-line no-lonely-if
|
|
159
|
+
if (isBuiltinType(finalBaseType.type)) {
|
|
160
|
+
/*
|
|
161
|
+
use transformUtils::toFinalBaseType for the moment,
|
|
162
|
+
as it is collects along the chain of types
|
|
163
|
+
attributes that need to be propagated
|
|
164
|
+
enum, length, scale, etc.
|
|
159
165
|
*/
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
attributes that need to be propagated
|
|
166
|
-
enum, length, scale, etc.
|
|
167
|
-
*/
|
|
168
|
-
transformers.toFinalBaseType(node);
|
|
169
|
-
// node.type = finalType;
|
|
170
|
-
}
|
|
171
|
-
else if (node.type.ref) {
|
|
172
|
-
cloneElements(node, finalBaseType);
|
|
173
|
-
}
|
|
166
|
+
transformers.toFinalBaseType(node);
|
|
167
|
+
// node.type = finalType;
|
|
168
|
+
}
|
|
169
|
+
else if (node.type.ref) {
|
|
170
|
+
cloneElements(node, finalBaseType);
|
|
174
171
|
}
|
|
175
172
|
}
|
|
176
173
|
}
|
|
@@ -397,7 +397,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
397
397
|
if (!draftAdminDataEntity) {
|
|
398
398
|
draftAdminDataEntity = createAndAddDraftAdminDataEntity();
|
|
399
399
|
model.definitions['DRAFT.DraftAdministrativeData'] = draftAdminDataEntity;
|
|
400
|
-
if (options.draftMessages && options.transformation === 'odata' &&
|
|
400
|
+
if (options.draftMessages && options.transformation === 'odata' && options.odataVersion === 'v4' &&
|
|
401
401
|
!model.definitions['DRAFT.DraftAdministrativeData_DraftMessage'])
|
|
402
402
|
model.definitions['DRAFT.DraftAdministrativeData_DraftMessage'] = createDraftAdminDataMessagesType();
|
|
403
403
|
|
|
@@ -493,7 +493,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
493
493
|
draftIsProcessedByMe.DraftIsProcessedByMe['@Common.Label'] = '{i18n>Draft_DraftIsProcessedByMe}';
|
|
494
494
|
addElement(draftIsProcessedByMe, artifact, artifactName);
|
|
495
495
|
|
|
496
|
-
if (options.transformation !== 'odata' || options.draftMessages) {
|
|
496
|
+
if (options.transformation !== 'odata' || (options.draftMessages && options.odataVersion === 'v4')) {
|
|
497
497
|
const messages = { DraftMessages: { } };
|
|
498
498
|
if (options.transformation === 'odata')
|
|
499
499
|
messages.DraftMessages = { items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } };
|
|
@@ -888,12 +888,11 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
888
888
|
* @param {CSN.Model} csn
|
|
889
889
|
*/
|
|
890
890
|
function rewriteBuiltinTypeRef(csn) {
|
|
891
|
-
const special$self = !csn?.definitions?.$self && '$self';
|
|
892
891
|
applyTransformations(csn, {
|
|
893
892
|
type: (parent, _prop, type) => {
|
|
894
893
|
if (type?.ref && (
|
|
895
894
|
isBuiltinType(type.ref[0]) ||
|
|
896
|
-
type.ref[0] ===
|
|
895
|
+
type.ref[0] === '$self')
|
|
897
896
|
)
|
|
898
897
|
parent.type = type.ref[0];
|
|
899
898
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "CDS (Core Data Services) compiler and backends",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"deployHdbcdsGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdbcds test3/test.deploy.git-diffs.js",
|
|
34
34
|
"deployHdiGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdi test3/test.deploy.git-diffs.js",
|
|
35
35
|
"gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
|
|
36
|
-
"coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
|
|
37
|
-
"coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
|
|
36
|
+
"coverage": "cross-env npx nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && npx nyc report --reporter=lcov",
|
|
37
|
+
"coverage:piper": "cross-env npx nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && npx nyc report --reporter=cobertura && npx nyc report --reporter=lcov",
|
|
38
38
|
"lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ && node scripts/linter/lintConstants.js && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
|
|
39
39
|
"lint:edmx": "node scripts/odata/lint-edmx-v4.js",
|
|
40
40
|
"tslint": "tsc --pretty -p .",
|