@sap/cds-compiler 6.9.3 → 7.0.1
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 +76 -2
- package/bin/cdsc.js +4 -33
- package/doc/IncompatibleChanges_v7.md +639 -0
- package/lib/api/main.js +4 -56
- package/lib/api/options.js +5 -15
- package/lib/api/validate.js +1 -0
- package/lib/base/builtins.js +1 -2
- package/lib/base/csnRefs.js +2 -6
- package/lib/base/message-registry.js +82 -76
- package/lib/base/messages.js +8 -5
- package/lib/base/optionProcessor.js +2 -72
- package/lib/base/specialOptions.js +20 -17
- package/lib/checks/defaultValues.js +1 -39
- package/lib/checks/hasPersistedElements.js +19 -3
- package/lib/checks/parameters.js +0 -34
- package/lib/checks/selectItems.js +2 -38
- package/lib/checks/typeParameters.js +162 -0
- package/lib/checks/validator.js +5 -8
- package/lib/compiler/assert-consistency.js +19 -5
- package/lib/compiler/checks.js +47 -43
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +102 -111
- package/lib/compiler/generate.js +4 -8
- package/lib/compiler/populate.js +4 -7
- package/lib/compiler/propagator.js +9 -9
- package/lib/compiler/resolve.js +205 -7
- package/lib/compiler/shared.js +76 -82
- package/lib/compiler/tweak-assocs.js +102 -22
- package/lib/compiler/utils.js +57 -12
- package/lib/compiler/xpr-rewrite.js +2 -15
- package/lib/edm/annotations/edmJson.js +14 -10
- package/lib/edm/annotations/genericTranslation.js +3 -1
- package/lib/edm/annotations/preprocessAnnotations.js +9 -26
- package/lib/edm/csn2edm.js +27 -20
- package/lib/edm/edmUtils.js +25 -0
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2237 -2241
- package/lib/gen/Dictionary.json +17 -2
- package/lib/json/from-csn.js +67 -52
- package/lib/json/to-csn.js +28 -25
- package/lib/language/textUtils.js +0 -13
- package/lib/main.d.ts +22 -59
- package/lib/main.js +1 -1
- package/lib/model/csnUtils.js +9 -8
- package/lib/parsers/AstBuildingParser.js +45 -55
- package/lib/parsers/Lexer.js +2 -0
- package/lib/parsers/identifiers.js +0 -9
- package/lib/render/toCdl.js +41 -40
- package/lib/render/toSql.js +8 -1
- package/lib/render/utils/common.js +1 -1
- package/lib/render/utils/sql.js +2 -3
- package/lib/tool-lib/enrichCsn.js +1 -2
- package/lib/transform/db/applyTransformations.js +7 -5
- package/lib/transform/db/assertUnique.js +8 -51
- package/lib/transform/db/associations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -15
- package/lib/transform/db/expansion.js +9 -12
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/groupByOrderBy.js +0 -16
- package/lib/transform/db/views.js +57 -161
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdata.js +25 -14
- package/lib/transform/forRelationalDB.js +93 -301
- package/lib/transform/localized.js +33 -102
- package/lib/transform/odata/flattening.js +11 -2
- package/lib/transform/transformUtils.js +25 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
- package/package.json +2 -2
- package/lib/render/toHdbcds.js +0 -1810
|
@@ -61,58 +61,20 @@ function usesMixinAssociation( query, association, associationName ) {
|
|
|
61
61
|
/**
|
|
62
62
|
* @param {CSN.Model} csn
|
|
63
63
|
* @param {CSN.Options} options
|
|
64
|
-
* @param {{error: Function, info: Function}}
|
|
64
|
+
* @param {{error: Function, info: Function}} _messageFunctions
|
|
65
65
|
* @returns {(query: CSN.Query, artifact: CSN.Artifact, artName: string, path: CSN.Path) => void} Transformer function for views
|
|
66
66
|
*/
|
|
67
|
-
function getViewTransformer( csn, options,
|
|
67
|
+
function getViewTransformer( csn, options, _messageFunctions ) {
|
|
68
68
|
const csnUtils = getUtils(csn);
|
|
69
69
|
const {
|
|
70
|
-
get$combined,
|
|
70
|
+
get$combined,
|
|
71
71
|
inspectRef, queryOrMain, // csnRefs
|
|
72
72
|
} = csnUtils;
|
|
73
|
-
const { error, info } = messageFunctions;
|
|
74
|
-
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
75
73
|
|
|
76
74
|
return transformViewOrEntity;
|
|
77
75
|
|
|
78
76
|
/**
|
|
79
|
-
*
|
|
80
|
-
* check all queries/subqueries for mixin publishing inside of unions -> forbidden in hdbcds
|
|
81
|
-
*
|
|
82
|
-
* @param {CSN.Query} query
|
|
83
|
-
* @param {CSN.Elements} elements
|
|
84
|
-
* @param {CSN.Path} path
|
|
85
77
|
*/
|
|
86
|
-
function checkForMixinPublishing( query, elements, path ) {
|
|
87
|
-
for (const elementName in elements) {
|
|
88
|
-
const element = elements[elementName];
|
|
89
|
-
if (element.target) {
|
|
90
|
-
let colLocation;
|
|
91
|
-
for (let i = 0; i < query.SELECT.columns.length; i++) {
|
|
92
|
-
const col = query.SELECT.columns[i];
|
|
93
|
-
if (col.ref && col.ref.length === 1) {
|
|
94
|
-
if (!colLocation && col.ref[0] === elementName)
|
|
95
|
-
colLocation = i;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (col.as === elementName)
|
|
99
|
-
colLocation = i;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (colLocation) {
|
|
103
|
-
const matchingCol = query.SELECT.columns[colLocation];
|
|
104
|
-
const possibleMixinName = matchingCol.ref[0];
|
|
105
|
-
const isMixin = query.SELECT.mixin[possibleMixinName] !== undefined;
|
|
106
|
-
if (element.target && isMixin) {
|
|
107
|
-
error(null, path.concat([ 'columns', colLocation ]), { id: elementName, name: possibleMixinName, '#': possibleMixinName === elementName ? 'std' : 'renamed' }, {
|
|
108
|
-
std: 'Element $(ID) is a mixin association and can\'t be published in a UNION',
|
|
109
|
-
renamed: 'Element $(ID) is a mixin association ($(NAME))and can\'t be published in a UNION',
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
78
|
|
|
117
79
|
/**
|
|
118
80
|
* For things that are not explicitly found in the columns but still present in the elements, add them to the columnMap.
|
|
@@ -158,8 +120,7 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
158
120
|
}
|
|
159
121
|
|
|
160
122
|
/**
|
|
161
|
-
*
|
|
162
|
-
* create the __clone for publishing stuff.
|
|
123
|
+
* Create the __clone for publishing association elements.
|
|
163
124
|
*
|
|
164
125
|
* @todo Factor out the checks
|
|
165
126
|
* @param {CSN.Query} query
|
|
@@ -169,95 +130,63 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
169
130
|
* @param {CSN.Element} elem
|
|
170
131
|
* @param {string} elemName
|
|
171
132
|
* @param {CSN.Path} elementsPath Path pointing to elements
|
|
172
|
-
* @param {CSN.Path} queryPath Path pointing to the query
|
|
173
133
|
*/
|
|
174
|
-
function handleAssociationElement( query, elements, columnMap, publishedMixins, elem, elemName, elementsPath,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (mixinElem.on) {
|
|
213
|
-
mixinElem.on = applyTransformationsOnNonDictionary(mixinElem, 'on', {
|
|
214
|
-
ref: (parent, prop, ref, refpath) => {
|
|
215
|
-
if (ref[0] === elemName) {
|
|
216
|
-
ref[0] = mixinElemName;
|
|
217
|
-
}
|
|
218
|
-
else if (!(ref[0] && ref[0].startsWith('$'))) {
|
|
134
|
+
function handleAssociationElement( query, elements, columnMap, publishedMixins, elem, elemName, elementsPath, _queryPath ) {
|
|
135
|
+
const isNotMixinByItself = checkIsNotMixinByItself(query, columnMap, elemName);
|
|
136
|
+
const { mixinElement, mixinName } = getMixinAssocOfQueryIfPublished(query, elem, elemName);
|
|
137
|
+
if (isNotMixinByItself || mixinElement !== undefined) {
|
|
138
|
+
// If the mixin is only published and not used, only display the __ clone. Kill the "original".
|
|
139
|
+
if (mixinElement !== undefined && !usesMixinAssociation(query, elem, elemName))
|
|
140
|
+
delete query.SELECT.mixin[mixinName];
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
// Create an unused alias name for the MIXIN - use 3 _ to avoid collision with usings
|
|
144
|
+
let mixinElemName = `___${ mixinName || elemName }`;
|
|
145
|
+
while (elements[mixinElemName])
|
|
146
|
+
mixinElemName = `_${ mixinElemName }`;
|
|
147
|
+
|
|
148
|
+
// Copy the association element to the MIXIN clause under its alias name
|
|
149
|
+
// Needs to be a deep copy, as we transform the on-condition
|
|
150
|
+
const mixinElem = cloneCsnNonDict(elem, options);
|
|
151
|
+
|
|
152
|
+
if (query.SELECT && !query.SELECT.mixin)
|
|
153
|
+
query.SELECT.mixin = Object.create(null);
|
|
154
|
+
|
|
155
|
+
// Clone 'on'-condition, pre-pending '$projection' to paths where appropriate,
|
|
156
|
+
// and fixing the association alias just created
|
|
157
|
+
|
|
158
|
+
if (mixinElem.on) {
|
|
159
|
+
mixinElem.on = applyTransformationsOnNonDictionary(mixinElem, 'on', {
|
|
160
|
+
ref: (parent, prop, ref, refpath) => {
|
|
161
|
+
if (ref[0] === elemName) {
|
|
162
|
+
ref[0] = mixinElemName;
|
|
163
|
+
}
|
|
164
|
+
else if (!(ref[0] && ref[0].startsWith('$'))) {
|
|
165
|
+
ref.unshift('$projection');
|
|
166
|
+
}
|
|
167
|
+
else if (ref[0] && ref[0].startsWith('$')) {
|
|
168
|
+
// TODO: I think this is non-sense. Stuff with $ is either magic or must start with $self, right?
|
|
169
|
+
const { scope } = inspectRef(refpath);
|
|
170
|
+
if (scope !== '$magic' && scope !== '$self')
|
|
219
171
|
ref.unshift('$projection');
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
parent.ref = ref;
|
|
228
|
-
return ref;
|
|
229
|
-
},
|
|
230
|
-
}, {}, elementsPath.concat(elemName));
|
|
231
|
-
}
|
|
172
|
+
}
|
|
173
|
+
parent.ref = ref;
|
|
174
|
+
return ref;
|
|
175
|
+
},
|
|
176
|
+
}, {}, elementsPath.concat(elemName));
|
|
177
|
+
}
|
|
232
178
|
|
|
233
|
-
|
|
234
|
-
|
|
179
|
+
if (!mixinElem.$ignore)
|
|
180
|
+
columnMap[elemName] = { ref: [ mixinElemName ], as: elemName };
|
|
235
181
|
|
|
236
|
-
|
|
237
|
-
|
|
182
|
+
if (query.SELECT) {
|
|
183
|
+
query.SELECT.mixin[mixinElemName] = mixinElem;
|
|
238
184
|
|
|
239
|
-
|
|
240
|
-
}
|
|
185
|
+
publishedMixins.set(mixinElem, true);
|
|
241
186
|
}
|
|
242
187
|
}
|
|
243
188
|
}
|
|
244
189
|
|
|
245
|
-
/**
|
|
246
|
-
* If following an association, explicitly set the implicit alias
|
|
247
|
-
* due to an issue with HANA - only for hdbcds-hdbcds, I assume flattening
|
|
248
|
-
* takes care of this for the other cases already
|
|
249
|
-
*
|
|
250
|
-
* @param {CSN.Column} col
|
|
251
|
-
* @param {CSN.Path} path
|
|
252
|
-
*/
|
|
253
|
-
function addImplicitAliasWithAssoc( col, path ) {
|
|
254
|
-
if (!col.as && col.ref && col.ref.length > 1) {
|
|
255
|
-
const { links } = inspectRef(path);
|
|
256
|
-
if (links && links.slice(0, -1).some(({ art }) => art && isAssocOrComposition(art)))
|
|
257
|
-
col.as = getLastRefStepString(col.ref);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
190
|
/**
|
|
262
191
|
* If simply selecting from a param like `:param`, we need to add an implicit alias like `:param as param`
|
|
263
192
|
* due to an issue with HANA
|
|
@@ -298,16 +227,11 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
298
227
|
const elementsPath = elements === artifact.elements ? path.slice(0, 2).concat('elements') : path.concat('elements');
|
|
299
228
|
const queryPath = path;
|
|
300
229
|
|
|
301
|
-
let hasNonAssocElements = false;
|
|
302
230
|
const isSelect = query && query.SELECT;
|
|
303
231
|
const isProjection = !!artifact.projection || query && query.SELECT && !query.SELECT.columns;
|
|
304
232
|
const columnMap = getColumnMap(query, csnUtils);
|
|
305
233
|
const isSelectStar = query && query.SELECT && query.SELECT.columns && query.SELECT.columns.indexOf('*') !== -1;
|
|
306
234
|
|
|
307
|
-
// check all queries/subqueries for mixin publishing inside of unions -> forbidden in hdbcds
|
|
308
|
-
if (query && options.transformation === 'hdbcds' && query.SELECT && query.SELECT.mixin && path.indexOf('SET') !== -1)
|
|
309
|
-
checkForMixinPublishing(query, elements, path);
|
|
310
|
-
|
|
311
235
|
// Second walk through the entity elements: Deal with associations (might also result in new elements)
|
|
312
236
|
// Will be initialized JIT inside the elements-loop
|
|
313
237
|
let $combined;
|
|
@@ -321,9 +245,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
321
245
|
if (!columnMap[elemName])
|
|
322
246
|
addProjectionOrStarElement(query, isProjection, isSelectStar, $combined, columnMap, elemName);
|
|
323
247
|
}
|
|
324
|
-
// Views must have at least one element that is not an unmanaged assoc
|
|
325
|
-
if (!elem.on && !elem.$ignore)
|
|
326
|
-
hasNonAssocElements = true;
|
|
327
248
|
|
|
328
249
|
// (180 b) Create MIXINs for association elements in projections or views (those that are not mixins by themselves)
|
|
329
250
|
// CDXCORE-585: Allow mixin associations to be used and published in parallel
|
|
@@ -331,21 +252,14 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
331
252
|
handleAssociationElement(query, elements, columnMap, publishedMixins, elem, elemName, elementsPath, queryPath);
|
|
332
253
|
}
|
|
333
254
|
|
|
334
|
-
if (query && !hasNonAssocElements) {
|
|
335
|
-
// Complain if there are no elements other than unmanaged associations or associations without keys.
|
|
336
|
-
error('def-missing-element', [ 'definitions', artName ], { '#': 'view' });
|
|
337
|
-
}
|
|
338
|
-
|
|
339
255
|
if (isSelect) {
|
|
340
256
|
// Build new columns from the column map - bring elements and columns back in sync basically
|
|
341
257
|
query.SELECT.columns = Object.keys(elements).filter(elem => !elements[elem].$ignore && !(elements[elem].target && ignoreAssociations)).map(key => stripLeadingSelf(columnMap[key]));
|
|
342
258
|
// If following an association, explicitly set the implicit alias
|
|
343
259
|
// due to an issue with HANA - this seems to only have an effect on ref files with hdbcds-hdbcds, so only run then
|
|
344
260
|
const columnProcessors = [];
|
|
345
|
-
if (options.transformation === '
|
|
261
|
+
if (options.transformation === 'sql' && options.sqlDialect === 'hana')
|
|
346
262
|
columnProcessors.push(addImplicitAliasWithLonelyParam);
|
|
347
|
-
if (options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')
|
|
348
|
-
columnProcessors.push(addImplicitAliasWithAssoc);
|
|
349
263
|
|
|
350
264
|
if (columnProcessors.length > 0)
|
|
351
265
|
processColumns(columnProcessors, query.SELECT.columns, path.concat('columns'));
|
|
@@ -353,7 +267,7 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
353
267
|
delete query.SELECT.excluding; // just to make the output of the new transformer the same as the old
|
|
354
268
|
|
|
355
269
|
// A2J turned usages into JOINs, we must now remove all non-published mixins (i.e. only keep the clones)
|
|
356
|
-
if (query.SELECT.mixin
|
|
270
|
+
if (query.SELECT.mixin) {
|
|
357
271
|
for (const [ name, mixin ] of Object.entries(query.SELECT.mixin)) {
|
|
358
272
|
if (!publishedMixins.has(mixin))
|
|
359
273
|
delete query.SELECT.mixin[name];
|
|
@@ -363,21 +277,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
363
277
|
}
|
|
364
278
|
}
|
|
365
279
|
|
|
366
|
-
/**
|
|
367
|
-
* Walk the given path and check if we are in a UNION.
|
|
368
|
-
* This will return true when it is called on the subquery inside of a SET.args property.
|
|
369
|
-
*
|
|
370
|
-
* @param {CSN.Path} path
|
|
371
|
-
* @returns {boolean}
|
|
372
|
-
*/
|
|
373
|
-
function isUnion( path ) {
|
|
374
|
-
const subquery = path[path.length - 1];
|
|
375
|
-
const queryIndex = path[path.length - 2];
|
|
376
|
-
const args = path[path.length - 3];
|
|
377
|
-
const unionOperator = path[path.length - 4];
|
|
378
|
-
return path.length > 3 && (subquery === 'SET' || subquery === 'SELECT') && typeof queryIndex === 'number' && queryIndex >= 0 && args === 'args' && unionOperator === 'SET';
|
|
379
|
-
}
|
|
380
|
-
|
|
381
280
|
/**
|
|
382
281
|
* Strip of leading $self of the ref
|
|
383
282
|
*
|
|
@@ -487,16 +386,13 @@ function getColumnMap( query, csnUtils ) {
|
|
|
487
386
|
*
|
|
488
387
|
* @param {object} query
|
|
489
388
|
* @param {object} csnUtils
|
|
490
|
-
* @param {CSN.Options}
|
|
389
|
+
* @param {CSN.Options} _options
|
|
491
390
|
*/
|
|
492
|
-
function ensureColumnNames( query, csnUtils,
|
|
391
|
+
function ensureColumnNames( query, csnUtils, _options ) {
|
|
493
392
|
const select = query.SELECT || query.projection;
|
|
494
393
|
for (const col of select?.columns || []) {
|
|
495
394
|
if (col !== '*' && !columnAlias(col)) {
|
|
496
|
-
|
|
497
|
-
col.as = csnUtils.getColumnName(col);
|
|
498
|
-
else
|
|
499
|
-
setProp(col, 'as', csnUtils.getColumnName(col));
|
|
395
|
+
setProp(col, 'as', csnUtils.getColumnName(col));
|
|
500
396
|
}
|
|
501
397
|
}
|
|
502
398
|
}
|
|
@@ -239,7 +239,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
239
239
|
// Note that we may need to do the HANA transformation steps for managed associations
|
|
240
240
|
// (foreign key field generation, generatedFieldName, creating ON-condition) by hand,
|
|
241
241
|
// because the corresponding transformation steps have already been done on all artifacts
|
|
242
|
-
// when we come here).
|
|
242
|
+
// when we come here).
|
|
243
243
|
/**
|
|
244
244
|
* The given association has a key named DraftUUID
|
|
245
245
|
*
|
|
@@ -273,7 +273,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
const draftUUIDKey = getDraftUUIDKey(draftAdministrativeData.DraftAdministrativeData);
|
|
276
|
-
if (
|
|
276
|
+
if (draftUUIDKey) {
|
|
277
277
|
const source = csn.definitions[draftAdministrativeData.DraftAdministrativeData.target];
|
|
278
278
|
const sourceElement = source.elements[draftUUIDKey.ref[0]];
|
|
279
279
|
const targetElement = {};
|
|
@@ -33,7 +33,7 @@ const { addLocalizationViews } = require('./localized');
|
|
|
33
33
|
const { cloneFullCsn } = require('../base/cloneCsn');
|
|
34
34
|
const { csnRefs } = require('../base/csnRefs');
|
|
35
35
|
const replaceForeignKeyRefsInExpressionAnnotations = require('./odata/foreignKeyRefsInXprAnnos');
|
|
36
|
-
const { isAnnotationExpression, primaryExprProperties } = require('../base/builtins');
|
|
36
|
+
const { isAnnotationExpression, primaryExprProperties, isBuiltinType } = require('../base/builtins');
|
|
37
37
|
|
|
38
38
|
// Transformation for ODATA. Expects a CSN 'inputModel', processes it for ODATA.
|
|
39
39
|
// The result should be suitable for consumption by EDMX processors (annotations and metadata)
|
|
@@ -70,8 +70,9 @@ const { isAnnotationExpression, primaryExprProperties } = require('../base/built
|
|
|
70
70
|
// (EdmPreproc candidate, check with RT if @Core.Computed required by them)
|
|
71
71
|
// - Rename shorthand annotations according to a builtin list (EdmPreproc Candidate)
|
|
72
72
|
// e.g. @label -> @Common.Label
|
|
73
|
-
// -
|
|
74
|
-
// association with @Common.ValueList.viaAssociation
|
|
73
|
+
// - For managed associations with a target that is annotated with @cds.odata.valuelist: annotate the
|
|
74
|
+
// association with @Common.ValueList.viaAssociation. Must happen before "flattening" so that
|
|
75
|
+
// the annotation is copied to the FKs .(EdmPreproc Candidate)
|
|
75
76
|
// - Check for @Analytics.Measure and @Aggregation.default (Linter check candidate, remove)
|
|
76
77
|
// - Check annotations. If annotation starts with '@sap...' it must have a string or boolean value
|
|
77
78
|
// (Linter check candidate)
|
|
@@ -230,7 +231,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
230
231
|
= flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
|
|
231
232
|
|
|
232
233
|
const allMgdAssocDefs = flattening.allInOneFlattening(csn, refFlattener, adaptRefs,
|
|
233
|
-
inspectRef, getFinalTypeInfo, isExternalServiceMember,
|
|
234
|
+
inspectRef, getFinalTypeInfo, isExternalServiceMember, messageFunctions, csnUtils, options);
|
|
234
235
|
flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs,
|
|
235
236
|
inspectRef, effectiveType, csnUtils, error, options,
|
|
236
237
|
{ // skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
@@ -317,7 +318,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
317
318
|
// hana to allow naming mode "hdbcds"
|
|
318
319
|
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana');
|
|
319
320
|
|
|
320
|
-
forEachMemberRecursively(def, (member, memberName, propertyName) => {
|
|
321
|
+
forEachMemberRecursively(def, (member, memberName, propertyName, path) => {
|
|
321
322
|
// Annotate elements, foreign keys, parameters, etc. with their DB names if requested
|
|
322
323
|
// Only these are actually required and don't annotate virtual elements in entities or types
|
|
323
324
|
// as they have no DB representation (although in views)
|
|
@@ -330,6 +331,9 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
330
331
|
memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
|
|
331
332
|
}
|
|
332
333
|
|
|
334
|
+
|
|
335
|
+
checkForComplexTypesWithDefaultValues(member, memberName, path);
|
|
336
|
+
|
|
333
337
|
processDynamicFieldControlAnnotations(member);
|
|
334
338
|
|
|
335
339
|
// Mark fields with @odata.on.insert/update as @Core.Computed
|
|
@@ -383,6 +387,14 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
383
387
|
//--------------------------------------------------------------------
|
|
384
388
|
// HELPER SECTION STARTS HERE
|
|
385
389
|
|
|
390
|
+
|
|
391
|
+
// in structured OData, elements which are structured are not allowed to have default values
|
|
392
|
+
function checkForComplexTypesWithDefaultValues(member, memberName, path) {
|
|
393
|
+
const resolvedType = getFinalTypeInfo(member.type);
|
|
394
|
+
if (structuredOData && !isBuiltinType(resolvedType?.type) && member.default !== undefined)
|
|
395
|
+
error('odata-unexpected-default-struct', path, { name: memberName });
|
|
396
|
+
}
|
|
397
|
+
|
|
386
398
|
// Transform @readonly/@mandatory/@disabled into @Common.FieldControl annotation
|
|
387
399
|
// with a when/then/else expression consisting of the input from the annotations.
|
|
388
400
|
function processDynamicFieldControlAnnotations(node) {
|
|
@@ -406,7 +418,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
406
418
|
};
|
|
407
419
|
|
|
408
420
|
const fieldControl = {
|
|
409
|
-
'=': true,
|
|
410
421
|
xpr: createFieldControlExpression(definedAnnotations),
|
|
411
422
|
};
|
|
412
423
|
|
|
@@ -591,12 +602,12 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
591
602
|
|
|
592
603
|
if (shouldSet(minVal)) {
|
|
593
604
|
setAnnotation(node, '@Validation.Minimum', minVal);
|
|
594
|
-
if (min
|
|
605
|
+
if (typeof min === 'object' && min.val !== undefined)
|
|
595
606
|
setAnnotation(node, '@Validation.Minimum.@Validation.Exclusive', true);
|
|
596
607
|
}
|
|
597
608
|
if (shouldSet(maxVal)) {
|
|
598
609
|
setAnnotation(node, '@Validation.Maximum', maxVal);
|
|
599
|
-
if (max
|
|
610
|
+
if (typeof max === 'object' && max.val !== undefined)
|
|
600
611
|
setAnnotation(node, '@Validation.Maximum.@Validation.Exclusive', true);
|
|
601
612
|
}
|
|
602
613
|
}
|
|
@@ -653,17 +664,17 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
653
664
|
}
|
|
654
665
|
}
|
|
655
666
|
|
|
656
|
-
//
|
|
657
|
-
// with @Common.ValueList.viaAssociation.
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
// of this function).
|
|
667
|
+
// If member is a managed association whose target has @cds.odata.valuelist
|
|
668
|
+
// and that is navigable (@odata.navigable), then annotate it with @Common.ValueList.viaAssociation.
|
|
669
|
+
// During flattening the anno will be copied to the association's FKs.
|
|
670
|
+
// Don't add anno to unmanaged assocs, as value list annotation only applies to Property, not NavProperty
|
|
661
671
|
function addCommonValueListviaAssociation(member, memberName) {
|
|
662
672
|
const vlAnno = '@Common.ValueList.viaAssociation';
|
|
663
673
|
if (isAssociation(member)) {
|
|
674
|
+
const managed = member.on === undefined;
|
|
664
675
|
const navigable = member['@odata.navigable'] !== false; // navigable disabled only if explicitly set to false
|
|
665
676
|
const targetDef = getCsnDef(member.target);
|
|
666
|
-
if (navigable && targetDef['@cds.odata.valuelist'] && !member[vlAnno])
|
|
677
|
+
if (managed && navigable && targetDef['@cds.odata.valuelist'] && !member[vlAnno])
|
|
667
678
|
setAnnotation(member, vlAnno, { '=': memberName });
|
|
668
679
|
}
|
|
669
680
|
}
|