@sap/cds-compiler 4.7.6 → 4.9.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 +63 -3
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +28 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +24 -1
- package/lib/api/main.js +119 -46
- package/lib/api/options.js +51 -0
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +116 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +76 -46
- package/lib/base/messages.js +121 -35
- package/lib/base/model.js +4 -7
- package/lib/checks/actionsFunctions.js +3 -3
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +5 -3
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +8 -56
- package/lib/compiler/assert-consistency.js +11 -7
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +105 -29
- package/lib/compiler/define.js +37 -25
- package/lib/compiler/extend.js +35 -12
- package/lib/compiler/index.js +9 -10
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +24 -18
- package/lib/compiler/resolve.js +47 -45
- package/lib/compiler/shared.js +61 -21
- package/lib/compiler/tweak-assocs.js +15 -90
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +149 -71
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +7 -7
- package/lib/edm/edmInboundChecks.js +57 -5
- package/lib/edm/edmPreprocessor.js +54 -25
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +138 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2085 -1989
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +21 -11
- package/lib/json/to-csn.js +8 -4
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +23 -16
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +90 -14
- package/lib/main.js +9 -1
- package/lib/model/cloneCsn.js +21 -9
- package/lib/model/csnRefs.js +153 -42
- package/lib/model/csnUtils.js +14 -11
- package/lib/model/enrichCsn.js +4 -2
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +135 -122
- package/lib/optionProcessor.js +49 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +6 -3
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +34 -1
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +11 -3
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +11 -10
- package/lib/transform/effective/misc.js +45 -14
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +29 -12
- package/lib/transform/forRelationalDB.js +104 -113
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +228 -107
- package/lib/transform/odata/toFinalBaseType.js +10 -26
- package/lib/transform/odata/typesExposure.js +41 -25
- package/lib/transform/parseExpr.js +4 -7
- package/lib/transform/transformUtils.js +50 -43
- package/lib/transform/translateAssocsToJoins.js +48 -48
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- package/lib/utils/.eslintrc.json +0 -7
|
@@ -616,10 +616,10 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
|
|
|
616
616
|
else if (typeof xpr.$env === 'number') {
|
|
617
617
|
if (xpr.$scope === 'mixin')
|
|
618
618
|
return '';
|
|
619
|
-
return error(null, xpr.$path, '$env with number is not handled yet -
|
|
619
|
+
return error(null, xpr.$path, '$env with number is not handled yet - report this error!');
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
-
return error(null, xpr.$path, 'Boolean $env is not handled yet -
|
|
622
|
+
return error(null, xpr.$path, 'Boolean $env is not handled yet - report this error!');
|
|
623
623
|
}
|
|
624
624
|
else if (xpr.ref) {
|
|
625
625
|
throw new ModelError('Missing $env and missing leading artifact ref - throwing to trigger recompilation!');
|
|
@@ -62,10 +62,9 @@ function usesMixinAssociation( query, association, associationName ) {
|
|
|
62
62
|
* @param {CSN.Model} csn
|
|
63
63
|
* @param {CSN.Options} options
|
|
64
64
|
* @param {{error: Function, info: Function}} messageFunctions
|
|
65
|
-
* @param {Function} transformCommon For the time being: Pass from outside
|
|
66
65
|
* @returns {(query: CSN.Query, artifact: CSN.Artifact, artName: string, path: CSN.Path) => void} Transformer function for views
|
|
67
66
|
*/
|
|
68
|
-
function getViewTransformer( csn, options, messageFunctions
|
|
67
|
+
function getViewTransformer( csn, options, messageFunctions ) {
|
|
69
68
|
const csnUtils = getUtils(csn);
|
|
70
69
|
const {
|
|
71
70
|
get$combined, isAssocOrComposition,
|
|
@@ -237,8 +236,6 @@ function getViewTransformer( csn, options, messageFunctions, transformCommon ) {
|
|
|
237
236
|
// Copy the association element to the MIXIN clause under its alias name
|
|
238
237
|
// Needs to be a deep copy, as we transform the on-condition
|
|
239
238
|
const mixinElem = cloneCsnNonDict(elem, options);
|
|
240
|
-
// Perform common transformations on the newly generated MIXIN element (won't be reached otherwise)
|
|
241
|
-
transformCommon(mixinElem, mixinElemName);
|
|
242
239
|
|
|
243
240
|
if (query.SELECT && !query.SELECT.mixin)
|
|
244
241
|
query.SELECT.mixin = Object.create(null);
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CompilerAssertion } = require('../../base/error');
|
|
4
|
+
|
|
5
|
+
const directMappings = {
|
|
6
|
+
'@Common.IsDayOfCalendarMonth': replace('@Semantics.calendar.dayOfMonth'),
|
|
7
|
+
'@Common.IsDayOfCalendarYear': replace('@Semantics.calendar.dayOfYear'),
|
|
8
|
+
'@Common.IsCalendarWeek': replace('@Semantics.calendar.week'),
|
|
9
|
+
'@Common.IsCalendarMonth': replace('@Semantics.calendar.month'),
|
|
10
|
+
'@Common.IsCalendarQuarter': replace('@Semantics.calendar.quarter'),
|
|
11
|
+
'@Common.IsCalendarHalfyear': replace('@Semantics.calendar.halfyear'),
|
|
12
|
+
'@Common.IsCalendarYear': replace('@Semantics.calendar.year'),
|
|
13
|
+
'@Common.IsCalendarYearWeek': replace('@Semantics.calendar.yearWeek'),
|
|
14
|
+
'@Common.IsCalendarYearMonth': replace('@Semantics.calendar.yearMonth'),
|
|
15
|
+
'@Common.IsCalendarYearQuarter': replace('@Semantics.calendar.yearQuarter'),
|
|
16
|
+
'@Common.IsCalendarYearHalfyear': replace('@Semantics.calendar.yearHalfyear'),
|
|
17
|
+
'@Common.IsCalendarDate': replace('@Semantics.date'),
|
|
18
|
+
'@Common.IsFiscalYearVariant': replace('@Semantics.yearVariant'),
|
|
19
|
+
'@Common.IsFiscalPeriod': replace('@Semantics.period'),
|
|
20
|
+
'@Common.IsFiscalYear': replace('@Semantics.year'),
|
|
21
|
+
'@Common.IsFiscalYearPeriod': replace('@Semantics.yearPeriod'),
|
|
22
|
+
'@Common.IsFiscalQuarter': replace('@Semantics.quarter'),
|
|
23
|
+
'@Common.IsFiscalYearQuarter': replace('@Semantics.yearQuarter'),
|
|
24
|
+
'@Common.IsFiscalWeek': replace('@Semantics.week'),
|
|
25
|
+
'@Common.IsFiscalYearWeek': replace('@Semantics.yearWeek'),
|
|
26
|
+
'@Common.IsDayOfFiscalYear': replace('@Semantics.dayOfYear'),
|
|
27
|
+
'@Measures.ISOCurrency': (csn, artifact, element, oldAnno) => {
|
|
28
|
+
const { targetElement } = getAnnoRefTarget(csn, artifact, element[oldAnno]);
|
|
29
|
+
if (refPointsToThisArtifact(csn, artifact, element, oldAnno)) {
|
|
30
|
+
replace('@Semantics.amount.currencyCode')(csn, artifact, element, oldAnno);
|
|
31
|
+
if (targetElement && targetElement['@Semantics.currencyCode'] === undefined)
|
|
32
|
+
targetElement['@Semantics.currencyCode'] = true;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
'@Measures.Unit': (csn, artifact, element, oldAnno) => {
|
|
36
|
+
const { targetElement } = getAnnoRefTarget(csn, artifact, element[oldAnno]);
|
|
37
|
+
if (refPointsToThisArtifact(csn, artifact, element, oldAnno)) {
|
|
38
|
+
replace('@Semantics.quantity.unitOfMeasure')(csn, artifact, element, oldAnno);
|
|
39
|
+
if (targetElement && targetElement['@Semantics.unitOfMeasure'] === undefined)
|
|
40
|
+
targetElement['@Semantics.unitOfMeasure'] = true;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
'@UI.IsImageURL': replace('@Semantics.imageUrl'),
|
|
44
|
+
'@Common.ValueList.CollectionPath': (csn, artifact, element) => {
|
|
45
|
+
if (!element.target && element['@Consumption.valueHelpDefinition'] === undefined) {
|
|
46
|
+
if (element['@Common.ValueList.Parameters'] && Array.isArray(element['@Common.ValueList.Parameters'])) {
|
|
47
|
+
const InOutParameters = element['@Common.ValueList.Parameters'].filter(param => param.$Type === 'Common.ValueListParameterInOut');
|
|
48
|
+
|
|
49
|
+
if (InOutParameters.length === 1) {
|
|
50
|
+
element['@Consumption.valueHelpDefinition'] = [ {
|
|
51
|
+
name: element['@Common.ValueList.CollectionPath'],
|
|
52
|
+
} ];
|
|
53
|
+
|
|
54
|
+
delete element['@Common.ValueList.CollectionPath'];
|
|
55
|
+
delete element['@Common.ValueList.Label'];
|
|
56
|
+
|
|
57
|
+
element['@Consumption.valueHelpDefinition'][0].element = element['@Common.ValueList.Parameters'][0].ValueListProperty;
|
|
58
|
+
delete element['@Common.ValueList.Parameters'];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
'@Common.TextFor': replace('@Semantics.text', true),
|
|
64
|
+
'@Common.IsLanguageIdentifier': replaceIf('@Semantics.language', true, (csn, artifact, element, anno) => !!element[anno]),
|
|
65
|
+
// We need to set two different annos here, depending on the value -> need a custom replacer
|
|
66
|
+
'@Common.Text': (csn, artifact, element, oldAnno) => {
|
|
67
|
+
const { targetArtifact, targetElement } = getAnnoRefTarget(csn, artifact, element[oldAnno]);
|
|
68
|
+
if (targetArtifact === artifact && !element['@ObjectModel.text.element'] && !targetElement['@Semantics.text']) {
|
|
69
|
+
element['@ObjectModel.text.element'] = element[oldAnno];
|
|
70
|
+
if (targetElement['@Semantics.text'] === undefined)
|
|
71
|
+
targetElement['@Semantics.text'] = true;
|
|
72
|
+
delete element['@Common.Text'];
|
|
73
|
+
}
|
|
74
|
+
else if (targetArtifact && targetElement && !element['@ObjectModel.text.association'] && !targetElement['@Semantics.text']) {
|
|
75
|
+
element['@ObjectModel.text.association'] = element[oldAnno];
|
|
76
|
+
if (targetElement['@Semantics.text'] === undefined)
|
|
77
|
+
targetElement['@Semantics.text'] = true;
|
|
78
|
+
delete element['@Common.Text'];
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @param {CSN.Model} csn
|
|
86
|
+
* @param {CSN.Artifact} artifact
|
|
87
|
+
* @param {CSN.Element} element
|
|
88
|
+
* @param {object} anno
|
|
89
|
+
* @returns {boolean}
|
|
90
|
+
*/
|
|
91
|
+
function refPointsToThisArtifact( csn, artifact, element, anno ) {
|
|
92
|
+
const { targetArtifact } = getAnnoRefTarget(csn, artifact, element[anno]);
|
|
93
|
+
return targetArtifact && targetArtifact === artifact;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Walk the possible annotation ref and return the artifact and element it points to
|
|
98
|
+
*
|
|
99
|
+
* @param {CSN.Model} csn
|
|
100
|
+
* @param {CSN.Artifact} startArtifact
|
|
101
|
+
* @param {object} annoValue
|
|
102
|
+
* @returns {object}
|
|
103
|
+
*/
|
|
104
|
+
function getAnnoRefTarget( csn, startArtifact, annoValue ) {
|
|
105
|
+
if (!annoValue || !annoValue['='])
|
|
106
|
+
return { targetArtifact: undefined, targetElement: undefined };
|
|
107
|
+
|
|
108
|
+
const steps = annoValue['='].split('.');
|
|
109
|
+
let base = startArtifact;
|
|
110
|
+
let element;
|
|
111
|
+
for (const step of steps) {
|
|
112
|
+
if (!base.elements)
|
|
113
|
+
return { targetArtifact: undefined, targetElement: undefined };
|
|
114
|
+
element = base.elements[step];
|
|
115
|
+
if (!element)
|
|
116
|
+
return { targetArtifact: undefined, targetElement: undefined };
|
|
117
|
+
if (element.target)
|
|
118
|
+
base = csn.definitions[element.target];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { targetArtifact: base, targetElement: element };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get the function to replace oldAnno with newAnno on carrier.
|
|
126
|
+
*
|
|
127
|
+
* - If available, use "replacement" as value.
|
|
128
|
+
* - Only do replacement if "condition" returns true
|
|
129
|
+
* - Possibly set additional annotations via "additional"
|
|
130
|
+
* @param {string} newAnno
|
|
131
|
+
* @param {any} replacement
|
|
132
|
+
* @param {Function} [condition]
|
|
133
|
+
* @param {Function} [additional]
|
|
134
|
+
* @returns {Function}
|
|
135
|
+
*/
|
|
136
|
+
function replace( newAnno, replacement, condition = () => true, additional = () => true ) {
|
|
137
|
+
return function replaceAnnotationPrefix(csn, artifact, carrier, oldAnno) {
|
|
138
|
+
if (carrier[newAnno] === undefined && condition(csn, artifact, carrier, oldAnno, newAnno)) {
|
|
139
|
+
carrier[newAnno] = replacement || carrier[oldAnno];
|
|
140
|
+
additional(carrier, oldAnno, newAnno);
|
|
141
|
+
delete carrier[oldAnno];
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the function to replace oldAnno with newAnno on carrier.
|
|
148
|
+
*
|
|
149
|
+
* - If available, use "replacement" as value.
|
|
150
|
+
* - Only do replacement if "condition" returns true
|
|
151
|
+
*
|
|
152
|
+
* @param {string} newAnno
|
|
153
|
+
* @param {any} replacement
|
|
154
|
+
* @param {Function} condition
|
|
155
|
+
* @returns {Function}
|
|
156
|
+
*/
|
|
157
|
+
function replaceIf( newAnno, replacement, condition ) {
|
|
158
|
+
return replace( newAnno, replacement, condition );
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
*
|
|
163
|
+
* @param {CSN.Model} csn
|
|
164
|
+
* @returns {object} Transfomer object for applyTransformations
|
|
165
|
+
*/
|
|
166
|
+
function remapODataAnnotations( csn ) {
|
|
167
|
+
/**
|
|
168
|
+
*
|
|
169
|
+
* @param {CSN.Artifact} artifact
|
|
170
|
+
* @param {CSN.Element} element Element to process
|
|
171
|
+
*/
|
|
172
|
+
function remapAnnotationsOnElement( artifact, element ) {
|
|
173
|
+
if (element.elements && !element.$ignore) // We expect to only be called on flattened CSN - error if we encounter .elements!
|
|
174
|
+
throw new CompilerAssertion(`Expected a flat model. Found element with subelements: ${JSON.stringify(element)}`);
|
|
175
|
+
for (const prop in element) {
|
|
176
|
+
if (directMappings[prop])
|
|
177
|
+
directMappings[prop](csn, artifact, element, prop);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
elements: (parent, prop, elements, path) => {
|
|
183
|
+
const artifact = csn.definitions[path[1]];
|
|
184
|
+
if (artifact?.kind === 'entity') {
|
|
185
|
+
for (const elementName in elements)
|
|
186
|
+
remapAnnotationsOnElement(artifact, elements[elementName]);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
remapODataAnnotations,
|
|
194
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const {
|
|
4
|
+
getUtils, isAspect, mergeTransformers, applyTransformations,
|
|
5
|
+
} = require('../../model/csnUtils');
|
|
6
6
|
const transformUtils = require('../transformUtils');
|
|
7
7
|
const flattening = require('../db/flattening');
|
|
8
8
|
const types = require('./types');
|
|
@@ -14,6 +14,7 @@ const associations = require('./associations');
|
|
|
14
14
|
const generateDrafts = require('../draft/db');
|
|
15
15
|
const handleExists = require('../db/transformExists');
|
|
16
16
|
const misc = require('./misc');
|
|
17
|
+
const annotations = require('./annotations');
|
|
17
18
|
const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
|
|
18
19
|
const { cloneFullCsn } = require('../../model/cloneCsn');
|
|
19
20
|
|
|
@@ -30,15 +31,12 @@ const { cloneFullCsn } = require('../../model/cloneCsn');
|
|
|
30
31
|
* @returns {CSN.Model}
|
|
31
32
|
*/
|
|
32
33
|
function effectiveCsn( model, options, messageFunctions ) {
|
|
33
|
-
if (!isBetaEnabled(options, 'effectiveCsn'))
|
|
34
|
-
throw new CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
|
|
35
|
-
|
|
36
34
|
const csn = cloneFullCsn(model, options);
|
|
37
35
|
delete csn.vocabularies; // must not be set for effective CSN
|
|
38
36
|
messageFunctions.setModel(csn);
|
|
39
37
|
|
|
40
38
|
const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
41
|
-
queries.projectionToSELECTAndAddColumns(csn);
|
|
39
|
+
const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
|
|
42
40
|
|
|
43
41
|
let csnUtils = getUtils(csn, 'init-all');
|
|
44
42
|
|
|
@@ -63,7 +61,7 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
63
61
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
64
62
|
expansion.expandStructureReferences(csn, options, '_', messageFunctions, csnUtils);
|
|
65
63
|
|
|
66
|
-
const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
|
|
64
|
+
const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils, options);
|
|
67
65
|
|
|
68
66
|
// Remove properties attached by validator - they do not "grow" as the model grows.
|
|
69
67
|
cleanup();
|
|
@@ -80,8 +78,11 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
80
78
|
associations.managedToUnmanaged(csn, options, csnUtils, messageFunctions);
|
|
81
79
|
associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
|
|
82
80
|
generateDrafts(csn, options, '_', messageFunctions);
|
|
83
|
-
misc.attachPersistenceName(csn, options, csnUtils);
|
|
84
|
-
|
|
81
|
+
const transformers = mergeTransformers([ misc.attachPersistenceName(csn, options, csnUtils), options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options) ], null);
|
|
82
|
+
applyTransformations(csn, transformers, [], { skipIgnore: false });
|
|
83
|
+
|
|
84
|
+
if (!options.resolveProjections)
|
|
85
|
+
redoProjections.forEach(fn => fn());
|
|
85
86
|
|
|
86
87
|
messageFunctions.throwWithError();
|
|
87
88
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
|
|
4
|
+
getArtifactDatabaseNameOf, getElementDatabaseNameOf,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
/**
|
|
7
7
|
* Attach @cds.persistence.name to all artifacts and "things".
|
|
@@ -9,15 +9,34 @@ const {
|
|
|
9
9
|
* @param {CSN.Model} csn
|
|
10
10
|
* @param {CSN.Options} options
|
|
11
11
|
* @param {object} csnUtils
|
|
12
|
+
* @returns {object}
|
|
12
13
|
*/
|
|
13
14
|
function attachPersistenceName( csn, options, csnUtils ) {
|
|
14
15
|
const { addStringAnnotationTo } = csnUtils;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param {object} parent
|
|
20
|
+
* @param {string} prop
|
|
21
|
+
* @param {object} dict
|
|
22
|
+
* @param {CSN.Path} path
|
|
23
|
+
*/
|
|
24
|
+
function addToEachMember( parent, prop, dict, path ) {
|
|
25
|
+
const artifact = csn.definitions[path[1]];
|
|
26
|
+
if (artifact?.kind === 'entity') {
|
|
27
|
+
for (const memberName in dict)
|
|
28
|
+
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), dict[memberName]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
return {
|
|
33
|
+
kind: (parent, prop, kind, path) => {
|
|
34
|
+
if (kind === 'entity')
|
|
35
|
+
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(path[1], options.sqlMapping, csn, options.sqlDialect), parent);
|
|
36
|
+
},
|
|
37
|
+
elements: addToEachMember,
|
|
38
|
+
params: addToEachMember,
|
|
39
|
+
};
|
|
21
40
|
}
|
|
22
41
|
|
|
23
42
|
/**
|
|
@@ -39,20 +58,29 @@ function killProp( parent, prop ) {
|
|
|
39
58
|
* - includes
|
|
40
59
|
* - localized
|
|
41
60
|
* @param {CSN.Model} csn
|
|
61
|
+
* @param {CSN.Options} options
|
|
62
|
+
* @returns {object}
|
|
42
63
|
* @todo Callback-like architecture and merge with persistence name?
|
|
43
64
|
*/
|
|
44
|
-
function _removeDefinitionsAndProperties( csn ) {
|
|
45
|
-
const
|
|
65
|
+
function _removeDefinitionsAndProperties( csn, options ) {
|
|
66
|
+
const transformers = {
|
|
46
67
|
$ignore: (a, b, c, path, parentParent) => {
|
|
47
68
|
const tail = path[path.length - 1];
|
|
48
69
|
delete parentParent[tail];
|
|
49
70
|
},
|
|
50
71
|
kind: (artifact, a, b, path) => {
|
|
51
|
-
if (artifact.kind === 'aspect' || artifact.kind === 'type')
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
if (artifact.kind === 'aspect' || artifact.kind === 'type') {
|
|
73
|
+
if (artifact.elements || artifact.items || options.resolveSimpleTypes)
|
|
74
|
+
delete csn.definitions[path[1]];
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
if (artifact.kind === 'event') {
|
|
78
|
+
delete artifact.projection;
|
|
79
|
+
delete artifact.query;
|
|
80
|
+
}
|
|
81
|
+
if (artifact['@cds.persistence.skip'] === 'if-unused')
|
|
82
|
+
artifact['@cds.persistence.skip'] = false;
|
|
83
|
+
}
|
|
56
84
|
},
|
|
57
85
|
// Still used in flattenStructuredElements - in db/flattening.js
|
|
58
86
|
_flatElementNameWithDots: killProp,
|
|
@@ -64,13 +92,16 @@ function _removeDefinitionsAndProperties( csn ) {
|
|
|
64
92
|
// Set when we remove .key from temporal things, used in localized.js
|
|
65
93
|
$key: killProp,
|
|
66
94
|
includes: killProp,
|
|
67
|
-
localized: killProp,
|
|
68
95
|
enum: killProp,
|
|
69
96
|
keys: killProp,
|
|
70
97
|
excluding: killProp, // * is resolved, so has no effect anymore
|
|
98
|
+
targetAspect: killProp,
|
|
71
99
|
};
|
|
72
100
|
|
|
73
|
-
|
|
101
|
+
if (!options.keepLocalized)
|
|
102
|
+
transformers.localized = killProp;
|
|
103
|
+
|
|
104
|
+
return transformers;
|
|
74
105
|
}
|
|
75
106
|
|
|
76
107
|
|
|
@@ -17,10 +17,11 @@ const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
17
17
|
* @todo What about annotations on the type?
|
|
18
18
|
* @param {CSN.Model} csn will be transformed
|
|
19
19
|
* @param {object} csnUtils
|
|
20
|
+
* @param {CSN.Options} options
|
|
20
21
|
* @returns {Function} Callback to resolve things (actions and their returns) later - as for them, $self would lead to unresolvable constructs at this point
|
|
21
22
|
* so we can call this callback after flattening is done - then we can safely resolve their types.
|
|
22
23
|
*/
|
|
23
|
-
function resolveTypes( csn, csnUtils ) {
|
|
24
|
+
function resolveTypes( csn, csnUtils, options ) {
|
|
24
25
|
const { getFinalTypeInfo } = csnUtils;
|
|
25
26
|
const later = [];
|
|
26
27
|
applyTransformations(csn, {
|
|
@@ -51,7 +52,7 @@ function resolveTypes( csn, csnUtils ) {
|
|
|
51
52
|
*
|
|
52
53
|
* Drill down into .elements and .items
|
|
53
54
|
*
|
|
54
|
-
* @param {object} parent Object with a .type
|
|
55
|
+
* @param {object} parent Object with a .type property
|
|
55
56
|
*/
|
|
56
57
|
function resolveType( parent ) {
|
|
57
58
|
// TODO: I assume there can be cases with a type ref but still having .elements already? Subelement anno?
|
|
@@ -67,7 +68,7 @@ function resolveTypes( csn, csnUtils ) {
|
|
|
67
68
|
parent.items = cloneCsnNonDict(final.items);
|
|
68
69
|
delete parent.type;
|
|
69
70
|
}
|
|
70
|
-
else if (final?.type) {
|
|
71
|
+
else if (final?.type && (options.resolveSimpleTypes || parent.type.ref?.length > 1)) {
|
|
71
72
|
forEachKey(final, (key) => { // copy `type` + properties (default, etc.)
|
|
72
73
|
if (parent[key] === undefined || key === 'type')
|
|
73
74
|
parent[key] = final[key];
|
|
@@ -73,7 +73,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
73
73
|
timetrace.start('OData transformation');
|
|
74
74
|
|
|
75
75
|
// copy the model as we don't want to change the input model
|
|
76
|
-
|
|
76
|
+
const csn = cloneFullCsn(inputModel, options);
|
|
77
77
|
messageFunctions.setModel(csn);
|
|
78
78
|
|
|
79
79
|
const { message, error, warning, info, throwWithAnyError } = messageFunctions;
|
|
@@ -119,6 +119,11 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
119
119
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
120
120
|
enrichUniversalCsn(csn, options);
|
|
121
121
|
|
|
122
|
+
// - Generate artificial draft fields on a structured CSN if requested, flattening and struct
|
|
123
|
+
// expansion do their magic including foreign key generation and annotation propagation.
|
|
124
|
+
// Tenantenizer has to decorate the DraftAdministrativeData, so draft decoration must be done before.
|
|
125
|
+
generateDrafts(csn, options, services, messageFunctions);
|
|
126
|
+
|
|
122
127
|
if (options.tenantDiscriminator)
|
|
123
128
|
addTenantFields(csn, options);
|
|
124
129
|
|
|
@@ -171,9 +176,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
171
176
|
// rendering which may has to publish external definitions
|
|
172
177
|
expandToFinalBaseType(csn, transformers, csnUtils, services, options, error);
|
|
173
178
|
|
|
174
|
-
// - Generate artificial draft fields on a structured CSN if requested, flattening and struct
|
|
175
|
-
// expansion do their magic including foreign key generation and annotation propagation.
|
|
176
|
-
generateDrafts(csn, options, services, messageFunctions);
|
|
177
179
|
|
|
178
180
|
// Check if structured elements and managed associations are compared in an expression
|
|
179
181
|
// and expand these structured elements. This tuple expansion allows all other
|
|
@@ -182,15 +184,19 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
182
184
|
expandStructsInExpression(csn, { skipArtifact: isExternalServiceMember, drillRef: true });
|
|
183
185
|
|
|
184
186
|
if (!structuredOData) {
|
|
185
|
-
expansion.expandStructureReferences(csn, options, '_',
|
|
187
|
+
expansion.expandStructureReferences(csn, options, '_',
|
|
188
|
+
{ error, info, throwWithAnyError }, csnUtils,
|
|
189
|
+
{ skipArtifact: isExternalServiceMember });
|
|
186
190
|
const resolved = new WeakMap();
|
|
187
191
|
|
|
188
192
|
const { inspectRef, effectiveType } = csnRefs(csn);
|
|
189
|
-
const { adaptRefs, transformer: refFlattener } =
|
|
190
|
-
|
|
191
|
-
flattening.allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options);
|
|
193
|
+
const { adaptRefs, transformer: refFlattener } =
|
|
194
|
+
flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
|
|
192
195
|
|
|
196
|
+
flattening.allInOneFlattening(csn, refFlattener, adaptRefs,
|
|
197
|
+
inspectRef, isExternalServiceMember, error, csnUtils, options);
|
|
193
198
|
flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs,
|
|
199
|
+
inspectRef, effectiveType, csnUtils, error, options,
|
|
194
200
|
{ //skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
195
201
|
skipArtifact: isExternalServiceMember,
|
|
196
202
|
});
|
|
@@ -199,20 +205,30 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
199
205
|
// rewritten path expressions
|
|
200
206
|
forEachDefinition(csn, (def) => {
|
|
201
207
|
['elements', 'params'].forEach(dictName => {
|
|
202
|
-
if(def[
|
|
203
|
-
def[dictName] = def[
|
|
208
|
+
if(def[`$flat${dictName}`])
|
|
209
|
+
def[dictName] = def[`$flat${dictName}`];
|
|
204
210
|
})
|
|
205
211
|
if(def.$flatAnnotations) {
|
|
206
212
|
Object.entries(def.$flatAnnotations).forEach(([an, av]) => {
|
|
207
213
|
def[an] = av;
|
|
208
214
|
})
|
|
209
215
|
}
|
|
216
|
+
if(def.actions) {
|
|
217
|
+
Object.values(def.actions).forEach((action) => {
|
|
218
|
+
if(action.$flatAnnotations) {
|
|
219
|
+
Object.entries(action.$flatAnnotations).forEach(([an, av]) => {
|
|
220
|
+
action[an] = av;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
210
225
|
});
|
|
211
226
|
}
|
|
212
227
|
|
|
213
228
|
// TODO: add the generated foreign keys to the columns when we are in a view
|
|
214
229
|
// see db/views.js::addForeignKeysToColumns
|
|
215
|
-
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_',
|
|
230
|
+
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_',
|
|
231
|
+
!structuredOData, csnUtils,{ skipArtifact: isExternalServiceMember });
|
|
216
232
|
|
|
217
233
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
218
234
|
// To be done after handleManagedAssociationsAndCreateForeignKeys,
|
|
@@ -251,7 +267,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
251
267
|
// Annotate artifacts with their DB names if requested.
|
|
252
268
|
// Skip artifacts that have no DB equivalent anyway
|
|
253
269
|
if (options.sqlMapping && !(def.kind in skipPersNameKinds))
|
|
254
|
-
|
|
270
|
+
// hana to allow naming mode "hdbcds"
|
|
271
|
+
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana');
|
|
255
272
|
|
|
256
273
|
forEachMemberRecursively(def, (member, memberName, propertyName) => {
|
|
257
274
|
// Annotate elements, foreign keys, parameters, etc. with their DB names if requested
|