@sap/cds-compiler 6.2.2 → 6.3.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 +35 -0
- package/bin/cdsc.js +11 -4
- package/lib/api/options.js +1 -1
- package/lib/base/message-registry.js +36 -7
- package/lib/base/messages.js +11 -4
- package/lib/base/model.js +0 -1
- package/lib/checks/assocOutsideService.js +17 -30
- package/lib/checks/checkForTypes.js +0 -18
- package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
- package/lib/checks/onConditions.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +16 -15
- package/lib/checks/types.js +1 -1
- package/lib/checks/utils.js +30 -6
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/checks.js +47 -18
- package/lib/compiler/index.js +88 -6
- package/lib/compiler/resolve.js +7 -7
- package/lib/compiler/tweak-assocs.js +47 -25
- package/lib/gen/BaseParser.js +1 -1
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +381 -378
- package/lib/gen/Dictionary.json +0 -2
- package/lib/model/csnRefs.js +9 -4
- package/lib/model/csnUtils.js +67 -2
- package/lib/optionProcessor.js +2 -3
- package/lib/parsers/AstBuildingParser.js +5 -6
- package/lib/render/toCdl.js +10 -4
- package/lib/render/utils/common.js +4 -2
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/associations.js +37 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
- package/lib/transform/db/assocsToQueries/utils.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +37 -36
- package/lib/transform/draft/db.js +20 -20
- package/lib/transform/draft/odata.js +38 -40
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/flattening.js +40 -47
- package/lib/transform/effective/main.js +6 -4
- package/lib/transform/forOdata.js +135 -115
- package/lib/transform/forRelationalDB.js +151 -142
- package/lib/transform/localized.js +116 -109
- package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
- package/lib/transform/odata/createForeignKeys.js +73 -70
- package/lib/transform/odata/flattening.js +216 -200
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
- package/lib/transform/odata/toFinalBaseType.js +40 -39
- package/lib/transform/odata/typesExposure.js +151 -133
- package/lib/transform/odata/utils.js +7 -6
- package/lib/transform/parseExpr.js +165 -162
- package/lib/transform/transformUtils.js +184 -551
- package/lib/transform/translateAssocsToJoins.js +510 -571
- package/lib/transform/tupleExpansion.js +495 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +1 -1
- package/lib/base/cleanSymbols.js +0 -17
- package/lib/checks/nonexpandableStructured.js +0 -39
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
4
|
const { setProp } = require('../base/model');
|
|
5
5
|
const { forEachKey } = require('../utils/objectUtils');
|
|
6
|
-
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
7
6
|
const {
|
|
8
7
|
applyAnnotationsFromExtensions,
|
|
9
8
|
forEachDefinition,
|
|
@@ -15,29 +14,9 @@ const {
|
|
|
15
14
|
cloneCsnDict,
|
|
16
15
|
cloneCsnNonDict,
|
|
17
16
|
sortCsnDefinitionsForTests,
|
|
18
|
-
sortCsn
|
|
17
|
+
sortCsn,
|
|
19
18
|
} = require('../model/cloneCsn');
|
|
20
19
|
|
|
21
|
-
/**
|
|
22
|
-
* Indicator that a definition is localized and has a convenience view.
|
|
23
|
-
* art[_hasLocalizedView]'s value should be the name of the convenience view.
|
|
24
|
-
*/
|
|
25
|
-
const _hasLocalizedView = Symbol('_hasLocalizedView');
|
|
26
|
-
/**
|
|
27
|
-
* Whether a convenience view was generated for another view.
|
|
28
|
-
* In that case we have a _vertical_ view.
|
|
29
|
-
*/
|
|
30
|
-
const _isViewForView = Symbol('_isViewForView'); // $inferred = 'LOCALIZED-VERTICAL'
|
|
31
|
-
/**
|
|
32
|
-
* Whether a convenience view was generated for an entity that is localized.
|
|
33
|
-
* In that case we have a _horizontal_ view.
|
|
34
|
-
*/
|
|
35
|
-
const _isViewForEntity = Symbol('_isViewForEntity'); // $inferred = 'LOCALIZED-HORIZONTAL'
|
|
36
|
-
/**
|
|
37
|
-
* List of artifacts for which the view/entity is a target.
|
|
38
|
-
* Used to transitively create convenience views.
|
|
39
|
-
*/
|
|
40
|
-
const _targetFor = Symbol('_targetFor');
|
|
41
20
|
const annoPersistenceSkip = '@cds.persistence.skip';
|
|
42
21
|
|
|
43
22
|
/**
|
|
@@ -50,8 +29,10 @@ const annoPersistenceSkip = '@cds.persistence.skip';
|
|
|
50
29
|
/**
|
|
51
30
|
* Create transitive localized convenience views.
|
|
52
31
|
*
|
|
53
|
-
* A convenience view is created if the entity/view has a localized element[^1]
|
|
54
|
-
* or if it exposes an association leading to a localized-tagged target.
|
|
32
|
+
* A convenience view is created if (a) the entity/view has a localized element[^1]
|
|
33
|
+
* or (b) if it exposes an association leading to a localized-tagged target.
|
|
34
|
+
* The second part (b) is only performed if option `fewerLocalizedViews` is
|
|
35
|
+
* disabled.
|
|
55
36
|
*
|
|
56
37
|
* INTERNALS:
|
|
57
38
|
* We have three kinds of localized convenience views:
|
|
@@ -120,9 +101,33 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
120
101
|
// default is true, hence only check for explicitly disabled option
|
|
121
102
|
const ignoreAssocToLocalized = options.fewerLocalizedViews !== false;
|
|
122
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Indicator that a definition is localized and has a convenience view.
|
|
106
|
+
* localizedViewsFor[name]'s value should be the name of the convenience view.
|
|
107
|
+
* @type {Record<string, string>}
|
|
108
|
+
*/
|
|
109
|
+
const localizedViewFor = Object.create(null);
|
|
110
|
+
/**
|
|
111
|
+
* Whether a convenience view was generated for another view.
|
|
112
|
+
* In that case we have a _vertical_ view.
|
|
113
|
+
* @type {Record<string, boolean>}
|
|
114
|
+
*/
|
|
115
|
+
const createdForView = Object.create(null); // $inferred = 'LOCALIZED-VERTICAL'
|
|
116
|
+
/**
|
|
117
|
+
* Whether a convenience view was generated for an entity that is localized.
|
|
118
|
+
* In that case we have a _horizontal_ view.
|
|
119
|
+
* @type {Record<string, boolean>}
|
|
120
|
+
*/
|
|
121
|
+
const createdForEntity = Object.create(null); // $inferred = 'LOCALIZED-HORIZONTAL'
|
|
122
|
+
/**
|
|
123
|
+
* List of artifacts for which the view/entity is a target.
|
|
124
|
+
* Used to transitively create convenience views.
|
|
125
|
+
* @type {Record<string, string[]>}
|
|
126
|
+
*/
|
|
127
|
+
const targetFor = Object.create(null);
|
|
128
|
+
|
|
123
129
|
createDirectConvenienceViews(); // 1
|
|
124
130
|
createTransitiveConvenienceViews(); // 2 + 3
|
|
125
|
-
cleanDefinitionSymbols();
|
|
126
131
|
applyAnnotationsForLocalizedViews();
|
|
127
132
|
sortLocalizedForTests(csn, options);
|
|
128
133
|
messageFunctions.throwWithError();
|
|
@@ -169,16 +174,16 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
169
174
|
return;
|
|
170
175
|
}
|
|
171
176
|
|
|
172
|
-
|
|
177
|
+
localizedViewFor[artName] = viewName;
|
|
173
178
|
|
|
174
179
|
if (acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
175
180
|
return;
|
|
176
181
|
|
|
177
182
|
let view;
|
|
178
183
|
if (art.query || art.projection)
|
|
179
|
-
view = createLocalizedViewForView(art);
|
|
184
|
+
view = createLocalizedViewForView(art, viewName);
|
|
180
185
|
else
|
|
181
|
-
view = createLocalizedViewForEntity(art, artName, textElements);
|
|
186
|
+
view = createLocalizedViewForEntity(art, artName, viewName, textElements);
|
|
182
187
|
|
|
183
188
|
copyPersistenceAnnotations(view, art);
|
|
184
189
|
csn.definitions[viewName] = view;
|
|
@@ -191,10 +196,11 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
191
196
|
*
|
|
192
197
|
* @param {CSN.Definition} entity
|
|
193
198
|
* @param {string} entityName
|
|
199
|
+
* @param {string} viewName Name of the localized view.
|
|
194
200
|
* @param {string[]} [textElements]
|
|
195
201
|
* @returns {CSN.View}
|
|
196
202
|
*/
|
|
197
|
-
function createLocalizedViewForEntity( entity, entityName, textElements = [] ) {
|
|
203
|
+
function createLocalizedViewForEntity( entity, entityName, viewName, textElements = [] ) {
|
|
198
204
|
// Only use joins if requested and text elements are provided.
|
|
199
205
|
const shouldUseJoin = useJoins && !!textElements.length;
|
|
200
206
|
const columns = [ ];
|
|
@@ -209,14 +215,14 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
209
215
|
},
|
|
210
216
|
},
|
|
211
217
|
elements: cloneCsnDict(entity.elements, options),
|
|
212
|
-
[_isViewForEntity]: true,
|
|
213
218
|
};
|
|
214
219
|
copyLocation(convenienceView, entity);
|
|
215
220
|
copyLocation(convenienceView.query, entity);
|
|
221
|
+
createdForEntity[viewName] = true;
|
|
216
222
|
|
|
217
223
|
if (shouldUseJoin)
|
|
218
224
|
// Expand elements; (variant 1)
|
|
219
|
-
columns.push( ...columnsForEntityWithExcludeList( entity, 'L_0', textElements ) )
|
|
225
|
+
columns.push( ...columnsForEntityWithExcludeList( entity, 'L_0', textElements ) );
|
|
220
226
|
else
|
|
221
227
|
columns.push( '*' ); // (variant 2)
|
|
222
228
|
|
|
@@ -235,11 +241,9 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
235
241
|
|
|
236
242
|
return convenienceView;
|
|
237
243
|
|
|
238
|
-
|
|
239
244
|
function createFromClauseForEntity() {
|
|
240
|
-
if (!shouldUseJoin)
|
|
245
|
+
if (!shouldUseJoin)
|
|
241
246
|
return createColumnRef( [ entityName ], 'L');
|
|
242
|
-
}
|
|
243
247
|
|
|
244
248
|
const from = {
|
|
245
249
|
join: 'left',
|
|
@@ -247,7 +251,7 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
247
251
|
createColumnRef( [ entityName ], 'L_0'),
|
|
248
252
|
createColumnRef( [ textsEntityName(entityName) ], 'localized_1' ),
|
|
249
253
|
],
|
|
250
|
-
on: []
|
|
254
|
+
on: [],
|
|
251
255
|
};
|
|
252
256
|
|
|
253
257
|
for (const originalElement of textElements) {
|
|
@@ -266,37 +270,37 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
266
270
|
|
|
267
271
|
return from;
|
|
268
272
|
}
|
|
269
|
-
|
|
270
273
|
}
|
|
271
274
|
|
|
272
275
|
/**
|
|
273
|
-
* Create a localized convenience view for the given definition `
|
|
276
|
+
* Create a localized convenience view for the given definition `art`.
|
|
274
277
|
* Does _not_ rewrite references.
|
|
275
278
|
*
|
|
276
|
-
* @param {CSN.Definition} view
|
|
279
|
+
* @param {CSN.Definition} art View for which a convenience view should be created.
|
|
280
|
+
* @param {string} viewName Name of the to-be created convenience view.
|
|
277
281
|
* @returns {CSN.View}
|
|
278
282
|
*/
|
|
279
|
-
function createLocalizedViewForView(
|
|
283
|
+
function createLocalizedViewForView( art, viewName ) {
|
|
280
284
|
const convenienceView = {
|
|
281
285
|
kind: 'entity',
|
|
282
|
-
'@odata.draft.enabled': false
|
|
286
|
+
'@odata.draft.enabled': false,
|
|
283
287
|
};
|
|
284
288
|
|
|
285
|
-
if (
|
|
286
|
-
convenienceView.query = cloneCsnNonDict(
|
|
287
|
-
else if (
|
|
288
|
-
convenienceView.projection = cloneCsnNonDict(
|
|
289
|
+
if (art.query)
|
|
290
|
+
convenienceView.query = cloneCsnNonDict(art.query, options);
|
|
291
|
+
else if (art.projection)
|
|
292
|
+
convenienceView.projection = cloneCsnNonDict(art.projection, options);
|
|
289
293
|
|
|
290
|
-
convenienceView.elements = cloneCsnDict(
|
|
291
|
-
|
|
292
|
-
copyLocation(convenienceView,
|
|
294
|
+
convenienceView.elements = cloneCsnDict(art.elements, options);
|
|
295
|
+
createdForView[viewName] = true;
|
|
296
|
+
copyLocation(convenienceView, art);
|
|
293
297
|
|
|
294
298
|
Object.keys(convenienceView.elements).forEach((elemName) => {
|
|
295
299
|
addCoreComputedIfNecessary(convenienceView.elements, elemName);
|
|
296
300
|
});
|
|
297
301
|
|
|
298
|
-
if (
|
|
299
|
-
convenienceView.params = cloneCsnDict(
|
|
302
|
+
if (art.params)
|
|
303
|
+
convenienceView.params = cloneCsnDict(art.params, options);
|
|
300
304
|
|
|
301
305
|
return convenienceView;
|
|
302
306
|
}
|
|
@@ -309,14 +313,14 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
309
313
|
const mainName = shouldUseJoins ? 'L_0' : 'L';
|
|
310
314
|
const localizedNames = shouldUseJoins ? [ 'localized_1' ] : [ 'L', 'localized' ];
|
|
311
315
|
|
|
312
|
-
if (noCoalesce)
|
|
313
|
-
return createColumnRef( [...localizedNames, elementName], elementName );
|
|
314
|
-
|
|
316
|
+
if (noCoalesce)
|
|
317
|
+
return createColumnRef( [ ...localizedNames, elementName ], elementName );
|
|
318
|
+
|
|
315
319
|
|
|
316
320
|
return {
|
|
317
321
|
func: 'coalesce',
|
|
318
322
|
args: [
|
|
319
|
-
createColumnRef( [ ...localizedNames, elementName] ),
|
|
323
|
+
createColumnRef( [ ...localizedNames, elementName ] ),
|
|
320
324
|
createColumnRef( [ mainName, elementName ] ),
|
|
321
325
|
],
|
|
322
326
|
as: elementName,
|
|
@@ -366,7 +370,7 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
366
370
|
let keyCount = 0;
|
|
367
371
|
let textElements = [];
|
|
368
372
|
|
|
369
|
-
forEachGeneric(art, 'elements', (elem, elemName
|
|
373
|
+
forEachGeneric(art, 'elements', (elem, elemName, _prop) => {
|
|
370
374
|
if (elem.$ignore) // from SAP HANA backend
|
|
371
375
|
return;
|
|
372
376
|
|
|
@@ -382,8 +386,10 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
382
386
|
return null;
|
|
383
387
|
|
|
384
388
|
if (!isEntityPreprocessed( art )) {
|
|
385
|
-
messageFunctions.info(
|
|
386
|
-
|
|
389
|
+
messageFunctions.info(
|
|
390
|
+
null, artPath, { name: artName },
|
|
391
|
+
'Skipped creation of convenience view for $(NAME) because the artifact is missing localization elements'
|
|
392
|
+
);
|
|
387
393
|
return null;
|
|
388
394
|
}
|
|
389
395
|
|
|
@@ -391,18 +397,24 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
391
397
|
const textsEntity = csn.definitions[textsName];
|
|
392
398
|
|
|
393
399
|
if (!textsEntity) {
|
|
394
|
-
messageFunctions.info(
|
|
395
|
-
|
|
400
|
+
messageFunctions.info(
|
|
401
|
+
null, artPath, { name: artName },
|
|
402
|
+
'Skipped creation of convenience view for $(NAME) because its texts entity could not be found'
|
|
403
|
+
);
|
|
396
404
|
return null;
|
|
397
405
|
}
|
|
398
406
|
if (!isValidTextsEntity( textsEntity )) {
|
|
399
|
-
messageFunctions.info(
|
|
400
|
-
|
|
407
|
+
messageFunctions.info(
|
|
408
|
+
null, [ 'definitions', textsName ], { name: artName },
|
|
409
|
+
'Skipped creation of convenience view for $(NAME) because its texts entity does not appear to be valid'
|
|
410
|
+
);
|
|
401
411
|
return null;
|
|
402
412
|
}
|
|
403
413
|
if (!art[annoPersistenceSkip] && textsEntity[annoPersistenceSkip]) {
|
|
404
|
-
messageFunctions.message(
|
|
405
|
-
|
|
414
|
+
messageFunctions.message(
|
|
415
|
+
'anno-unexpected-localized-skip', artPath,
|
|
416
|
+
{ name: textsName, art: artName, anno: annoPersistenceSkip }
|
|
417
|
+
);
|
|
406
418
|
return null;
|
|
407
419
|
}
|
|
408
420
|
|
|
@@ -433,7 +445,7 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
433
445
|
*
|
|
434
446
|
* 1. For each view with elements that have `localized: true` markers:
|
|
435
447
|
* => add view to array `entities`
|
|
436
|
-
* For each view/entity with associations:
|
|
448
|
+
* For each view/entity with associations (if fewerLocalizedViews is false):
|
|
437
449
|
* - If target is NOT localized => add view/entity to target's `_targetFor` property
|
|
438
450
|
* - If target is localized => add view/entity to array `entities`
|
|
439
451
|
* 2. As long as `entities` has entries:
|
|
@@ -465,7 +477,7 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
465
477
|
if (isInLocalizedNamespace(artName))
|
|
466
478
|
// Ignore existing `localized.` views.
|
|
467
479
|
return;
|
|
468
|
-
if (
|
|
480
|
+
if (localizedViewFor[artName])
|
|
469
481
|
// Entity already has a convenience view.
|
|
470
482
|
return;
|
|
471
483
|
|
|
@@ -486,11 +498,12 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
486
498
|
}
|
|
487
499
|
else if (!ignoreAssocToLocalized && elem.target) {
|
|
488
500
|
// If the target has a localized view then we are localized as well.
|
|
501
|
+
// Only necessary if "fewerLocalizedView" is disabled.
|
|
489
502
|
const def = csn.definitions[elem.target];
|
|
490
503
|
if (!def)
|
|
491
504
|
continue;
|
|
492
505
|
|
|
493
|
-
if (
|
|
506
|
+
if (localizedViewFor[elem.target]) {
|
|
494
507
|
// The target may already be localized and if so, then add the artifact
|
|
495
508
|
// to the to-be-processed entities.
|
|
496
509
|
entities.push(artName);
|
|
@@ -498,12 +511,11 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
498
511
|
else {
|
|
499
512
|
// Otherwise the target view may become localized at a later point so
|
|
500
513
|
// we should add it to a reverse-dependency list.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
def[_targetFor].push(artName);
|
|
514
|
+
targetFor[elem.target] ??= [];
|
|
515
|
+
targetFor[elem.target].push(artName);
|
|
504
516
|
}
|
|
505
|
-
|
|
506
|
-
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
507
519
|
// recursive check
|
|
508
520
|
_collectFromElements(elem.elements);
|
|
509
521
|
}
|
|
@@ -518,16 +530,16 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
518
530
|
* @param {string} artName
|
|
519
531
|
*/
|
|
520
532
|
function createViewAndCollectSources( artName ) {
|
|
521
|
-
|
|
522
|
-
if (art[_hasLocalizedView])
|
|
533
|
+
if (localizedViewFor[artName]) {
|
|
523
534
|
// view/entity was already processed
|
|
524
535
|
return;
|
|
536
|
+
}
|
|
525
537
|
|
|
526
538
|
addLocalizedView(artName);
|
|
527
539
|
|
|
528
|
-
if (!ignoreAssocToLocalized &&
|
|
529
|
-
nextEntities.push(...
|
|
530
|
-
delete
|
|
540
|
+
if (!ignoreAssocToLocalized && targetFor[artName])
|
|
541
|
+
nextEntities.push(...targetFor[artName]);
|
|
542
|
+
delete targetFor[artName];
|
|
531
543
|
}
|
|
532
544
|
}
|
|
533
545
|
|
|
@@ -538,12 +550,12 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
538
550
|
* @param {string} artName
|
|
539
551
|
*/
|
|
540
552
|
function rewriteToLocalized( art, artName ) {
|
|
541
|
-
if (
|
|
553
|
+
if (createdForEntity[artName]) {
|
|
542
554
|
// For entity convenience views only references in elements need to be rewritten.
|
|
543
555
|
// a.k.a 'LOCALIZED-HORIZONTAL'
|
|
544
556
|
forEachGeneric(art, 'elements', elem => rewriteDirectRefPropsToLocalized(elem));
|
|
545
557
|
}
|
|
546
|
-
else if (
|
|
558
|
+
else if (createdForView[artName]) {
|
|
547
559
|
// For view convenience views (i.e. transitive views) we need to rewrite `from`
|
|
548
560
|
// references as well as need to handle `mixin` elements.
|
|
549
561
|
// a.k.a 'LOCALIZED-VERTICAL'
|
|
@@ -597,8 +609,8 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
597
609
|
}
|
|
598
610
|
else if (typeof val === 'string') {
|
|
599
611
|
const def = csn.definitions[val];
|
|
600
|
-
if (def &&
|
|
601
|
-
obj[prop] =
|
|
612
|
+
if (def && localizedViewFor[val])
|
|
613
|
+
obj[prop] = localizedViewFor[val];
|
|
602
614
|
}
|
|
603
615
|
}
|
|
604
616
|
}
|
|
@@ -614,20 +626,18 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
614
626
|
return;
|
|
615
627
|
const ref = Array.isArray(obj.ref) ? obj.ref[0] : obj.ref;
|
|
616
628
|
if (typeof ref === 'string') {
|
|
617
|
-
|
|
618
|
-
if (def && def[_hasLocalizedView]) {
|
|
629
|
+
if (localizedViewFor[ref]) {
|
|
619
630
|
if (Array.isArray(obj.ref))
|
|
620
|
-
obj.ref[0] =
|
|
631
|
+
obj.ref[0] = localizedViewFor[ref];
|
|
621
632
|
else
|
|
622
|
-
|
|
633
|
+
obj.ref = localizedViewFor[ref];
|
|
623
634
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
} else if (options.testMode) {
|
|
635
|
+
}
|
|
636
|
+
else if (ref.id) {
|
|
637
|
+
if (localizedViewFor[ref.id])
|
|
638
|
+
obj.ref[0].id = localizedViewFor[ref.id];
|
|
639
|
+
}
|
|
640
|
+
else if (options.testMode) {
|
|
631
641
|
throw new CompilerAssertion('Debug me: Unhandled reference during localized-rewrite!');
|
|
632
642
|
}
|
|
633
643
|
}
|
|
@@ -640,19 +650,13 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
640
650
|
return csn.definitions[artName].elements.texts.target;
|
|
641
651
|
}
|
|
642
652
|
|
|
643
|
-
function cleanDefinitionSymbols() {
|
|
644
|
-
forEachDefinition(csn, function cleanDefinition(definition) {
|
|
645
|
-
cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor);
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
|
|
649
653
|
/**
|
|
650
654
|
* In case that the user tried to annotate `localized.*` artifacts, apply them.
|
|
651
655
|
*/
|
|
652
656
|
function applyAnnotationsForLocalizedViews() {
|
|
653
657
|
applyAnnotationsFromExtensions(csn, {
|
|
654
658
|
override: true,
|
|
655
|
-
filter:
|
|
659
|
+
filter: name => name.startsWith('localized.'),
|
|
656
660
|
notFound(name, index) {
|
|
657
661
|
if (!ignoreUnknownExtensions) {
|
|
658
662
|
messageFunctions.message('ext-undefined-def', [ 'extensions', index ],
|
|
@@ -673,11 +677,11 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
673
677
|
const artName = defName.substring(localizedPrefix.length);
|
|
674
678
|
const art = csn.definitions[artName];
|
|
675
679
|
if (def[annoPersistenceSkip] && !art?.[annoPersistenceSkip]) {
|
|
676
|
-
messageFunctions.message( 'anno-unexpected-localized-skip', ['definitions', defName], {
|
|
680
|
+
messageFunctions.message( 'anno-unexpected-localized-skip', [ 'definitions', defName ], {
|
|
677
681
|
'#': 'view',
|
|
678
682
|
name: defName,
|
|
679
683
|
art: artName,
|
|
680
|
-
anno: annoPersistenceSkip
|
|
684
|
+
anno: annoPersistenceSkip,
|
|
681
685
|
});
|
|
682
686
|
}
|
|
683
687
|
}
|
|
@@ -734,9 +738,7 @@ function columnsForEntityWithExcludeList(entity, entityName, excludeList) {
|
|
|
734
738
|
// @ts-ignore
|
|
735
739
|
return Object.keys(entity.elements)
|
|
736
740
|
.filter(elementName => !excludeList.includes(elementName))
|
|
737
|
-
.map(elementName => {
|
|
738
|
-
return { ref: [ entityName, elementName ] };
|
|
739
|
-
});
|
|
741
|
+
.map(elementName => ({ ref: [ entityName, elementName ] }));
|
|
740
742
|
}
|
|
741
743
|
|
|
742
744
|
/**
|
|
@@ -758,7 +760,7 @@ function copyLocation(target, source) {
|
|
|
758
760
|
* @param {CSN.Artifact} source
|
|
759
761
|
*/
|
|
760
762
|
function copyPersistenceAnnotations(target, source) {
|
|
761
|
-
forEachKey(source, anno => {
|
|
763
|
+
forEachKey(source, (anno) => {
|
|
762
764
|
// Note:
|
|
763
765
|
// v3/v4: Because `.exists` is copied to the convenience view, it could
|
|
764
766
|
// lead to some localization views referencing non-existing ones.
|
|
@@ -788,13 +790,18 @@ function checkExistingLocalizationViews(csn, options, messageFunctions) {
|
|
|
788
790
|
if (!def.query && !def.projection) {
|
|
789
791
|
if (!name.endsWith('.texts')) {
|
|
790
792
|
hasNonViews = true;
|
|
791
|
-
messageFunctions.error(
|
|
792
|
-
'
|
|
793
|
+
messageFunctions.error(
|
|
794
|
+
'reserved-namespace-localized', [ 'definitions', name ], { name: 'localized' },
|
|
795
|
+
'The namespace $(NAME) is reserved for localization views'
|
|
796
|
+
);
|
|
793
797
|
}
|
|
794
|
-
}
|
|
798
|
+
}
|
|
799
|
+
else if (!hasExistingViews) {
|
|
795
800
|
hasExistingViews = true;
|
|
796
|
-
messageFunctions.info(
|
|
797
|
-
|
|
801
|
+
messageFunctions.info(
|
|
802
|
+
null, [ 'definitions', name ], {},
|
|
803
|
+
'Input CSN already contains localization views, no further ones will be created'
|
|
804
|
+
);
|
|
798
805
|
}
|
|
799
806
|
}
|
|
800
807
|
});
|
|
@@ -810,7 +817,7 @@ function isValidTextsEntity(entity) {
|
|
|
810
817
|
if (!entity)
|
|
811
818
|
return false;
|
|
812
819
|
const requiredTextsProps = [ 'locale' ];
|
|
813
|
-
return requiredTextsProps.some( prop => !!entity.elements[prop])
|
|
820
|
+
return requiredTextsProps.some( prop => !!entity.elements[prop]);
|
|
814
821
|
}
|
|
815
822
|
|
|
816
823
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { transformAnnotationExpression, implicitAs
|
|
3
|
+
const { transformAnnotationExpression, implicitAs } = require('../../model/csnUtils');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Used during annotating of foreign keys.
|
|
@@ -14,23 +14,27 @@ const { transformAnnotationExpression, implicitAs, } = require('../../model/csnU
|
|
|
14
14
|
* @param {CSN.Path} elementPath
|
|
15
15
|
*/
|
|
16
16
|
function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, elementPath) {
|
|
17
|
-
if(Array.isArray(generatedForeignKeys?.[0])) {
|
|
17
|
+
if (Array.isArray(generatedForeignKeys?.[0])) {
|
|
18
18
|
// ensure we are always called with an array of objects. TODO: Cleanup fk creation in for.effective to create array of objects
|
|
19
19
|
adaptAnnotationsRefs(remapToArrayOfObjects(generatedForeignKeys), csnUtils, { error }, elementPath);
|
|
20
|
-
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
21
22
|
const reportedErrorsForAnnoPath = {};
|
|
22
23
|
generatedForeignKeys.forEach((gfk, index) => {
|
|
23
|
-
Object.entries(gfk.foreignKey).forEach(([key, value]) => {
|
|
24
|
-
if (key[0] !== '@')
|
|
24
|
+
Object.entries(gfk.foreignKey).forEach(([ key, value ]) => {
|
|
25
|
+
if (key[0] !== '@')
|
|
26
|
+
return;
|
|
25
27
|
|
|
26
28
|
transformAnnotationExpression(gfk.foreignKey, key, {
|
|
27
29
|
ref: (_parent, _prop, ref, path, _p, _ppn, ctx) => {
|
|
28
30
|
// if the reference is a $self reference, we do nothing,
|
|
29
31
|
// as this is the way to tell that we do not reference the foreign key
|
|
30
|
-
if (ref[0] === '$self')
|
|
32
|
+
if (ref[0] === '$self')
|
|
33
|
+
return;
|
|
31
34
|
// if annotation was not propagated from the keys array during foreign keys creation,
|
|
32
35
|
// means that it is not a candidate for foreign key substitution
|
|
33
|
-
if (gfk.keyAnnotations !== null && !gfk.keyAnnotations.includes(key))
|
|
36
|
+
if (gfk.keyAnnotations !== null && !gfk.keyAnnotations.includes(key))
|
|
37
|
+
return;
|
|
34
38
|
|
|
35
39
|
const art = gfk.originalKey._art ||
|
|
36
40
|
csnUtils.inspectRef(elementPath ? path : getOriginatingKeyPath(gfk, path)).art; // OData uses getOriginatingKeyPath - as it relies on $path
|
|
@@ -39,15 +43,16 @@ function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, element
|
|
|
39
43
|
error('odata-anno-xpr-ref', path, { elemref: { ref }, anno: key, '#': 'fk_substitution' });
|
|
40
44
|
reportedErrorsForAnnoPath[path] = true;
|
|
41
45
|
}
|
|
42
|
-
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
43
48
|
const gfkForRef = findGeneratedForeignKeyForKeyRef(generatedForeignKeys, ref);
|
|
44
49
|
if (gfkForRef.length === 1) {
|
|
45
50
|
ref[0] = gfkForRef[0].prefix;
|
|
46
51
|
|
|
47
|
-
if (ctx?.annoExpr?.['='])
|
|
52
|
+
if (ctx?.annoExpr?.['='])
|
|
48
53
|
ctx.annoExpr['='] = true;
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
51
56
|
// check if the annotation reference points to a structure that has been expanded,
|
|
52
57
|
// if so -> report an error
|
|
53
58
|
const foundInOriginalRef = findOriginalRef(generatedForeignKeys.filter(gfk => gfk.originalKey.$originalKeyRef), ref);
|
|
@@ -59,8 +64,8 @@ function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, element
|
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
|
-
}
|
|
63
|
-
}, elementPath ? elementPath.concat(['keys', index]) : value?.$path?.slice(0, value.$path.length - 1)); // OData uses $path
|
|
67
|
+
},
|
|
68
|
+
}, elementPath ? elementPath.concat([ 'keys', index ]) : value?.$path?.slice(0, value.$path.length - 1)); // OData uses $path
|
|
64
69
|
});
|
|
65
70
|
});
|
|
66
71
|
}
|
|
@@ -88,9 +93,9 @@ function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, element
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
function remapToArrayOfObjects(generatedForeignKeys) {
|
|
91
|
-
return generatedForeignKeys.map(([ prefix, foreignKey, originalKey ]) => {
|
|
92
|
-
|
|
93
|
-
});
|
|
96
|
+
return generatedForeignKeys.map(([ prefix, foreignKey, originalKey ]) => ({
|
|
97
|
+
prefix, foreignKey, originalKey, keyAnnotations: null,
|
|
98
|
+
}));
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
module.exports = { adaptAnnotationsRefs };
|