@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/edm/csn2edm.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
/* eslint max-lines:off */
|
|
4
4
|
/* eslint max-statements-per-line:off */
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const NAVPROP_TRENNER = '_';
|
|
7
|
+
const VALUELIST_NAVPROP_PREFIX = '';
|
|
8
8
|
|
|
9
9
|
const edmUtils = require('./edmUtils.js')
|
|
10
10
|
const { initializeModel } = require('./edmPreprocessor.js');
|
|
@@ -25,11 +25,11 @@ function csn2edm(_csn, serviceName, _options) {
|
|
|
25
25
|
|
|
26
26
|
function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
27
27
|
// get us a fresh model copy that we can work with
|
|
28
|
-
|
|
28
|
+
const csn = cloneCsn(_csn, _options);
|
|
29
29
|
|
|
30
30
|
// use original options for messages; cloned CSN for semantic location
|
|
31
31
|
const messageFunctions = makeMessageFunction(csn, _options, 'to.edmx');
|
|
32
|
-
const { info, warning, error, throwWithError } = messageFunctions;
|
|
32
|
+
const { info, warning, error, message, throwWithError } = messageFunctions;
|
|
33
33
|
checkCSNVersion(csn, _options);
|
|
34
34
|
|
|
35
35
|
let rc = Object.create(null);
|
|
@@ -42,10 +42,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
42
42
|
setProp(csn.meta, 'options', _csn.meta.options);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
let [ allServices,
|
|
45
|
+
let [ allServices,
|
|
46
|
+
allSchemas,
|
|
47
|
+
whatsMyServiceRootName,
|
|
48
|
+
options ] = initializeModel(csn, _options, messageFunctions);
|
|
49
|
+
|
|
46
50
|
const Edm = require('./edm.js')(options, error);
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
const v = options.v;
|
|
49
53
|
if(Object.keys(allServices).length === 0) {
|
|
50
54
|
info(null, null, `No Services in model`);
|
|
51
55
|
return rc;
|
|
@@ -222,7 +226,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
222
226
|
}));
|
|
223
227
|
});
|
|
224
228
|
// Create annotations and distribute into Schemas
|
|
225
|
-
|
|
229
|
+
addAnnotations();
|
|
226
230
|
return edm
|
|
227
231
|
|
|
228
232
|
// Sort definitions into their schema container
|
|
@@ -282,8 +286,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
282
286
|
|
|
283
287
|
return Object.entries(allSchemas).reduce((references, [fqName, art]) => {
|
|
284
288
|
// add references
|
|
285
|
-
|
|
286
|
-
if(art.kind === 'reference' && mySchemaName && serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
|
|
289
|
+
if(art.kind === 'reference' && whatsMySchemaName(fqName) && serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
|
|
287
290
|
fqSchemaXRef.push(fqName);
|
|
288
291
|
references.push(art);
|
|
289
292
|
}
|
|
@@ -298,16 +301,17 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
298
301
|
// Same check for alias (if supported by us)
|
|
299
302
|
const reservedNames = ['Edm', 'odata', 'System', 'Transient'];
|
|
300
303
|
if(reservedNames.includes(schema.name)) {
|
|
301
|
-
warning('odata-spec-violation-namespace
|
|
302
|
-
|
|
304
|
+
warning('odata-spec-violation-namespace',
|
|
305
|
+
[ 'definitions', schema.name ], { names: reservedNames });
|
|
303
306
|
}
|
|
307
|
+
/** @type {any} */
|
|
304
308
|
const Schema = new Edm.Schema(v, schema.name, undefined /* unset alias */, schema._csn, /* annotations */ [], schema.container);
|
|
305
309
|
const EntityContainer = Schema._ec || (LeadSchema && LeadSchema._ec);
|
|
306
310
|
// now namespace and alias are used to create the fullQualified(name)
|
|
307
311
|
const schemaNamePrefix = schema.name + '.'
|
|
308
312
|
const schemaAliasPrefix = schemaNamePrefix;
|
|
309
313
|
const schemaCsn = schema;
|
|
310
|
-
|
|
314
|
+
const navigationProperties = [];
|
|
311
315
|
|
|
312
316
|
/* create the entitytypes and sets
|
|
313
317
|
Do not create an entity set if:
|
|
@@ -337,7 +341,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
337
341
|
}
|
|
338
342
|
|
|
339
343
|
// fetch all exising children names in a map
|
|
340
|
-
|
|
344
|
+
const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
|
|
341
345
|
if(acc[cur.Name] === undefined) {
|
|
342
346
|
acc[cur.Name] = [ cur ];
|
|
343
347
|
} else {
|
|
@@ -359,8 +363,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
359
363
|
|
|
360
364
|
// remove EntityContainer if empty
|
|
361
365
|
if(Schema._ec && Schema._ec._children.length === 0) {
|
|
362
|
-
|
|
363
|
-
Schema._children.splice(pos, 1);
|
|
366
|
+
Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
|
|
364
367
|
}
|
|
365
368
|
if(Schema._children.length === 0) {
|
|
366
369
|
// FIXME: Location for sub schemas?
|
|
@@ -369,8 +372,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
369
372
|
|
|
370
373
|
Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
|
|
371
374
|
if(refs.length > 1) {
|
|
372
|
-
|
|
373
|
-
error(null, ['definitions', artifactName], { name: Schema.Namespace },
|
|
375
|
+
error(null, ['definitions', `${Schema.Namespace}.${name}`], { name: Schema.Namespace },
|
|
374
376
|
'Duplicate name in Schema $(NAME)');
|
|
375
377
|
}
|
|
376
378
|
});
|
|
@@ -379,33 +381,33 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
379
381
|
|
|
380
382
|
function createEntityTypeAndSet(entityCsn)
|
|
381
383
|
{
|
|
382
|
-
|
|
383
|
-
|
|
384
|
+
const EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
|
|
385
|
+
const EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
|
|
384
386
|
|
|
385
|
-
|
|
387
|
+
const [ properties, hasStream ] = createProperties(entityCsn);
|
|
386
388
|
|
|
387
389
|
const loc = ['definitions', entityCsn.name];
|
|
388
390
|
const type = `${schema.name}.${EntityTypeName}`;
|
|
389
391
|
if(properties.length === 0) {
|
|
390
392
|
warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no properties');
|
|
391
393
|
} else if(entityCsn.$edmKeyPaths.length === 0) {
|
|
392
|
-
|
|
394
|
+
message('odata-spec-violation-no-key', loc);
|
|
393
395
|
}
|
|
394
396
|
properties.forEach(p => {
|
|
395
397
|
const pLoc = [...loc, 'elements', p.Name];
|
|
396
398
|
if(!p[p._typeName]) {
|
|
397
|
-
|
|
399
|
+
message('odata-spec-violation-type', pLoc);
|
|
398
400
|
}
|
|
399
401
|
if(p.Name === EntityTypeName) {
|
|
400
|
-
warning('odata-spec-violation-property-name', pLoc, {
|
|
402
|
+
warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
|
|
401
403
|
}
|
|
402
404
|
if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
|
|
403
|
-
warning('odata-spec-violation-array
|
|
405
|
+
warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
|
|
404
406
|
}
|
|
405
407
|
});
|
|
406
408
|
|
|
407
409
|
// construct EntityType attributes
|
|
408
|
-
|
|
410
|
+
const attributes = { Name : EntityTypeName };
|
|
409
411
|
|
|
410
412
|
// CDXCORE-CDXCORE-173
|
|
411
413
|
if(options.isV2() && hasStream)
|
|
@@ -418,7 +420,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
418
420
|
/** @type {object} */
|
|
419
421
|
let containerEntry;
|
|
420
422
|
|
|
421
|
-
if(isSingleton(entityCsn)) {
|
|
423
|
+
if(edmUtils.isSingleton(entityCsn, options.isV4())) {
|
|
422
424
|
containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
|
|
423
425
|
if(entityCsn['@odata.singleton.nullable'])
|
|
424
426
|
containerEntry.Nullable= true;
|
|
@@ -428,18 +430,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
428
430
|
}
|
|
429
431
|
|
|
430
432
|
// V4: Create NavigationPropertyBinding in EntitySet
|
|
431
|
-
if(options.isV4())
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
!edmUtils.isContainee(np._targetCsn) &&
|
|
437
|
-
!np._targetCsn.$proxy &&
|
|
438
|
-
!np._targetCsn.$externalRef &&
|
|
439
|
-
!(np._isCollection && isSingleton(np._targetCsn))
|
|
440
|
-
). forEach(np =>
|
|
441
|
-
containerEntry.append(np.createNavigationPropertyBinding(schemaNamePrefix)));
|
|
442
|
-
|
|
433
|
+
if(options.isV4()) {
|
|
434
|
+
entityCsn.$edmNPBs.forEach(npb => {
|
|
435
|
+
containerEntry.append(new Edm.NavigationPropertyBinding(v, npb))
|
|
436
|
+
});
|
|
437
|
+
}
|
|
443
438
|
EntityContainer.register(containerEntry);
|
|
444
439
|
}
|
|
445
440
|
|
|
@@ -448,38 +443,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
448
443
|
(options.isV4()) ? createActionV4(a, n, entityCsn)
|
|
449
444
|
: createActionV2(a, n, entityCsn)
|
|
450
445
|
});
|
|
451
|
-
|
|
452
|
-
function isSingleton(entityCsn) {
|
|
453
|
-
const singleton = entityCsn['@odata.singleton'];
|
|
454
|
-
const hasNullable = entityCsn['@odata.singleton.nullable'] !== undefined && entityCsn['@odata.singleton.nullable'] !== null;
|
|
455
|
-
return options.isV4() && singleton || ((singleton === undefined || singleton === null) && hasNullable);
|
|
456
|
-
}
|
|
457
446
|
}
|
|
458
447
|
|
|
459
448
|
// add bound/unbound actions/functions for V4
|
|
460
449
|
function createActionV4(actionCsn, name, entityCsn=undefined)
|
|
461
450
|
{
|
|
462
|
-
|
|
451
|
+
const iAmAnAction = actionCsn.kind === 'action';
|
|
463
452
|
|
|
464
|
-
|
|
453
|
+
const actionName = edmUtils.getBaseName(actionCsn.name);
|
|
465
454
|
|
|
466
|
-
|
|
455
|
+
const attributes = { Name: actionName, IsBound : false };
|
|
467
456
|
|
|
468
457
|
if(!iAmAnAction)
|
|
469
458
|
attributes.IsComposable = false;
|
|
470
459
|
|
|
471
460
|
/** @type {object} */
|
|
472
|
-
|
|
461
|
+
const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
|
|
473
462
|
: new Edm.FunctionDefinition(v, attributes);
|
|
474
463
|
|
|
475
464
|
// bpName is eventually used later for EntitySetPath
|
|
476
|
-
|
|
477
|
-
|
|
465
|
+
const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
|
|
466
|
+
const bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
|
|
478
467
|
|
|
479
468
|
if(entityCsn != undefined)
|
|
480
469
|
{
|
|
481
470
|
actionNode.IsBound = true;
|
|
482
|
-
|
|
471
|
+
const bpType = fullQualified(entityCsn.name);
|
|
483
472
|
// Binding Parameter: 'in' at first position in sequence, this is decisive!
|
|
484
473
|
if(actionCsn['@cds.odata.bindingparameter.collection'])
|
|
485
474
|
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true } ));
|
|
@@ -489,14 +478,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
489
478
|
else if(EntityContainer)// unbound => produce Action/FunctionImport
|
|
490
479
|
{
|
|
491
480
|
/** @type {object} */
|
|
492
|
-
|
|
481
|
+
const actionImport = iAmAnAction
|
|
493
482
|
? new Edm.ActionImport(v, { Name: actionName, Action : fullQualified(actionName) })
|
|
494
483
|
: new Edm.FunctionImport(v, { Name: actionName, Function : fullQualified(actionName) });
|
|
495
484
|
|
|
496
|
-
|
|
485
|
+
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
497
486
|
if(rt) // add EntitySet attribute only if return type is a non abstract entity
|
|
498
487
|
{
|
|
499
|
-
|
|
488
|
+
const definition = schemaCsn.definitions[rt];
|
|
500
489
|
if(definition && definition.kind === 'entity' && !definition.abstract)
|
|
501
490
|
{
|
|
502
491
|
actionImport.EntitySet = edmUtils.getBaseName(rt);
|
|
@@ -525,7 +514,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
525
514
|
function createActionV2(actionCsn, name, entityCsn=undefined)
|
|
526
515
|
{
|
|
527
516
|
/** @type {object} */
|
|
528
|
-
|
|
517
|
+
const functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
|
|
529
518
|
|
|
530
519
|
// inserted now to maintain attribute order with old odata generator...
|
|
531
520
|
/*
|
|
@@ -539,10 +528,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
539
528
|
*/
|
|
540
529
|
|
|
541
530
|
const actLoc = ['definitions', ...(entityCsn ? [entityCsn.name, 'actions', actionCsn.name] : [actionCsn.name])];
|
|
542
|
-
|
|
531
|
+
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
543
532
|
if(rt) // add EntitySet attribute only if return type is an entity
|
|
544
533
|
{
|
|
545
|
-
|
|
534
|
+
const defintion = schemaCsn.definitions[rt];
|
|
546
535
|
if(defintion && edmUtils.isEntity(defintion))
|
|
547
536
|
{
|
|
548
537
|
functionImport.EntitySet = rt.replace(schemaNamePrefix, '');
|
|
@@ -586,10 +575,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
586
575
|
const paramLoc = [...actLoc, 'params', parameterName];
|
|
587
576
|
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
588
577
|
if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
|
|
589
|
-
warning('odata-spec-violation-param
|
|
578
|
+
warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
|
|
590
579
|
}
|
|
591
580
|
if(param._isCollection) {
|
|
592
|
-
warning('odata-spec-violation-array
|
|
581
|
+
warning('odata-spec-violation-array', paramLoc, { api: 'OData V2' });
|
|
593
582
|
}
|
|
594
583
|
functionImport.append(param);
|
|
595
584
|
});
|
|
@@ -600,12 +589,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
600
589
|
function getReturnType(action)
|
|
601
590
|
{
|
|
602
591
|
// it is safe to assume that either type or items.type are set
|
|
603
|
-
|
|
592
|
+
const returns = action.returns.items || action.returns;
|
|
604
593
|
let type = returns.type;
|
|
605
594
|
if(type){
|
|
606
595
|
if(!isBuiltinType(type) && !['entity', 'view', 'type'].includes(csn.definitions[type].kind)){
|
|
607
596
|
const returnsLoc = [ ...actLoc, 'returns'];
|
|
608
|
-
warning('odata-spec-violation-returns
|
|
597
|
+
warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
|
|
609
598
|
}
|
|
610
599
|
type = edmUtils.mapCdsToEdmType(returns, messageFunctions, options.isV2());
|
|
611
600
|
}
|
|
@@ -618,19 +607,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
618
607
|
}
|
|
619
608
|
|
|
620
609
|
/**
|
|
621
|
-
* @param {object}
|
|
610
|
+
* @param {object} elementsCsn
|
|
611
|
+
* @param {object} edmParentCsn
|
|
622
612
|
* @returns {[object[], boolean]} Returns a [ [ Edm Properties ], boolean hasStream ]:
|
|
623
613
|
* array of Edm Properties
|
|
624
614
|
* boolean hasStream : true if at least one element has @Core.MediaType assignment
|
|
625
615
|
*/
|
|
626
|
-
function createProperties(
|
|
616
|
+
function createProperties(elementsCsn, edmParentCsn=elementsCsn)
|
|
627
617
|
{
|
|
628
|
-
|
|
618
|
+
const props = [];
|
|
629
619
|
let hasStream = false;
|
|
630
|
-
edmUtils.forAll(
|
|
620
|
+
edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
|
|
631
621
|
{
|
|
632
|
-
if(elementCsn.
|
|
633
|
-
setProp(elementCsn, '
|
|
622
|
+
if(elementCsn._edmParentCsn == undefined)
|
|
623
|
+
setProp(elementCsn, '_edmParentCsn', edmParentCsn);
|
|
634
624
|
|
|
635
625
|
if(!elementCsn._ignore) {
|
|
636
626
|
if(edmUtils.isAssociationOrComposition(elementCsn))
|
|
@@ -644,11 +634,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
644
634
|
// (undefined !== false) still evaluates to true
|
|
645
635
|
if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
|
|
646
636
|
{
|
|
647
|
-
|
|
637
|
+
const navProp = new Edm.NavigationProperty(v, {
|
|
648
638
|
Name: elementName,
|
|
649
639
|
Type: elementCsn._target.name
|
|
650
640
|
}, elementCsn);
|
|
651
|
-
|
|
652
641
|
props.push(navProp);
|
|
653
642
|
// save the navProp in the global array for late constraint building
|
|
654
643
|
navigationProperties.push(navProp);
|
|
@@ -667,7 +656,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
667
656
|
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
668
657
|
// V4: render property type 'Edm.Stream'
|
|
669
658
|
hasStream = true;
|
|
670
|
-
info(null, ['definitions',
|
|
659
|
+
info(null, ['definitions', elementsCsn.name], { name: elementsCsn.name, id: elementName, anno: '@Core.MediaType' },
|
|
671
660
|
'$(NAME): Property $(ID) annotated with $(ANNO) is removed from EDM in OData V2');
|
|
672
661
|
|
|
673
662
|
} else {
|
|
@@ -683,11 +672,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
683
672
|
function createComplexType(structuredTypeCsn)
|
|
684
673
|
{
|
|
685
674
|
// V4 attributes: Name, BaseType, Abstract, OpenType
|
|
686
|
-
|
|
675
|
+
const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
|
|
687
676
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
677
|
+
const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
|
|
678
|
+
const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
|
|
679
|
+
const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
|
|
691
680
|
const loc = ['definitions', structuredTypeCsn.name];
|
|
692
681
|
|
|
693
682
|
if(properties.length === 0) {
|
|
@@ -697,17 +686,17 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
697
686
|
properties.forEach(p => {
|
|
698
687
|
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p.Name ];
|
|
699
688
|
if(!p[p._typeName]) {
|
|
700
|
-
|
|
689
|
+
message('odata-spec-violation-type', pLoc);
|
|
701
690
|
}
|
|
702
691
|
if(p.Name === complexType.Name) {
|
|
703
|
-
warning('odata-spec-violation-property-name', pLoc, {
|
|
692
|
+
warning('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
|
|
704
693
|
}
|
|
705
694
|
if(options.isV2()) {
|
|
706
695
|
if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
|
|
707
|
-
warning('odata-spec-violation-array
|
|
696
|
+
warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
|
|
708
697
|
}
|
|
709
698
|
if(edmUtils.isAssociationOrComposition(p._csn)) {
|
|
710
|
-
warning('odata-spec-violation-assoc
|
|
699
|
+
warning('odata-spec-violation-assoc', pLoc, { api: 'OData V2' });
|
|
711
700
|
}
|
|
712
701
|
}
|
|
713
702
|
});
|
|
@@ -722,7 +711,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
722
711
|
function createTypeDefinition(typeCsn)
|
|
723
712
|
{
|
|
724
713
|
// derived types are already resolved to base types
|
|
725
|
-
|
|
714
|
+
const props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
|
|
726
715
|
const typeDef = new Edm.TypeDefinition(v, props, typeCsn );
|
|
727
716
|
Schema.append(typeDef);
|
|
728
717
|
}
|
|
@@ -744,7 +733,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
744
733
|
function addAssociation(navigationProperty)
|
|
745
734
|
{
|
|
746
735
|
let constraints = navigationProperty._csn._constraints;
|
|
747
|
-
let parentName = navigationProperty._csn.
|
|
736
|
+
let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
748
737
|
let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
749
738
|
let assocName = plainAssocName;
|
|
750
739
|
let i = 1;
|
|
@@ -760,7 +749,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
760
749
|
|
|
761
750
|
// The entity set name may not be the same as the type name (parameterized entities have
|
|
762
751
|
// differing set names (<T>Parameters => <T>, <T>Type => <T>Set)
|
|
763
|
-
let fromEntitySet = ( navigationProperty._csn.
|
|
752
|
+
let fromEntitySet = ( navigationProperty._csn._edmParentCsn.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
|
|
764
753
|
let toEntitySet = (navigationProperty._targetCsn.$entitySetName || toEntityType).replace(schemaNamePrefix, '');
|
|
765
754
|
|
|
766
755
|
// from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })
|
|
@@ -802,7 +791,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
802
791
|
[ fromEntityType, toEntityType ] = [ toEntityType, fromEntityType ];
|
|
803
792
|
[ fromEntitySet, toEntitySet ] = [ toEntitySet, fromEntitySet ];
|
|
804
793
|
|
|
805
|
-
parentName = forwardAssocCsn.
|
|
794
|
+
parentName = forwardAssocCsn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
806
795
|
assocName = plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
807
796
|
i = 1;
|
|
808
797
|
while(NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association)) {
|
|
@@ -817,56 +806,37 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
817
806
|
}
|
|
818
807
|
|
|
819
808
|
if(reuseAssoc)
|
|
820
|
-
{
|
|
821
|
-
// Example:
|
|
822
|
-
// entity E { key id: Integer; toF: association to F; };
|
|
823
|
-
// entity F { key id: Integer; toE: composition of E on toE.toF = $self; };
|
|
824
|
-
//
|
|
825
|
-
// Consider we're in NavigationProperty 'toE' which is the backlink to F.
|
|
826
|
-
// Then forwardAssocCsn is 'E_toF' with two Ends: E, F.
|
|
827
|
-
// Backlink F.toE is a composition, making E existentially dependant on F.
|
|
828
|
-
// So End E of Association E_toF (which is End[0]) receives Edm.OnDelete.
|
|
829
|
-
// Depending on the order of the navigation properties it might be that the
|
|
830
|
-
// forward Edm.Association has not yet been produced. In this case Edm.OnDelete
|
|
831
|
-
// is parked at the forward NavigationProperty.
|
|
832
|
-
|
|
833
|
-
if(!forwardAssocCsn._NavigationProperty._edmAssociation && navigationProperty._csn.type === 'cds.Composition')
|
|
834
|
-
{
|
|
835
|
-
// TODO: to be specified via @sap.on.delete
|
|
836
|
-
forwardAssocCsn._NavigationProperty.set( { _OnDelete: new Edm.OnDelete(v, { Action: 'Cascade' }) } );
|
|
837
|
-
}
|
|
838
809
|
return;
|
|
839
|
-
}
|
|
840
810
|
|
|
841
811
|
// Create Association and AssociationSet if this is not a backlink association.
|
|
842
812
|
// Store association at navigation property because in case the Ends must be modified
|
|
843
813
|
// later by the partner (backlink) association
|
|
844
|
-
|
|
814
|
+
const edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
|
|
845
815
|
[ fromRole, fullQualified(fromEntityType) ],
|
|
846
816
|
[ toRole, fullQualified(toEntityType) ],
|
|
847
817
|
constraints._multiplicity );
|
|
848
818
|
if(NamesInSchemaXRef[assocName] === undefined) {
|
|
849
|
-
NamesInSchemaXRef[assocName] = [
|
|
819
|
+
NamesInSchemaXRef[assocName] = [ edmAssociation ];
|
|
850
820
|
}
|
|
851
821
|
else {
|
|
852
|
-
|
|
822
|
+
NamesInSchemaXRef[assocName].push(edmAssociation);
|
|
853
823
|
}
|
|
854
824
|
// Add ReferentialConstraints if any
|
|
855
825
|
if(!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
|
|
856
826
|
// A managed composition is treated as association
|
|
857
827
|
if(navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
|
|
858
|
-
|
|
828
|
+
edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
|
|
859
829
|
toRole, fromRole, constraints.constraints));
|
|
860
830
|
}
|
|
861
831
|
else {
|
|
862
|
-
|
|
832
|
+
edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
|
|
863
833
|
fromRole, toRole, constraints.constraints));
|
|
864
834
|
}
|
|
865
835
|
}
|
|
866
836
|
|
|
867
|
-
Schema.append(
|
|
837
|
+
Schema.append(edmAssociation);
|
|
868
838
|
if(EntityContainer && !navigationProperty._targetCsn.$proxy) {
|
|
869
|
-
|
|
839
|
+
const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
|
|
870
840
|
fromRole, toRole, fromEntitySet, toEntitySet);
|
|
871
841
|
if(navigationProperty._csn._SetAttributes)
|
|
872
842
|
assocSet.setSapVocabularyAsAttributes(navigationProperty._csn._SetAttributes);
|
|
@@ -880,6 +850,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
880
850
|
return schemaAliasPrefix + name.replace(schemaNamePrefix, '')
|
|
881
851
|
}
|
|
882
852
|
}
|
|
853
|
+
|
|
854
|
+
// generate the Edm.Annotations tree and append it to the corresponding schema
|
|
855
|
+
function addAnnotations() {
|
|
856
|
+
let { annos, usedVocabularies } = translate.csn2annotationEdm(csn, serviceCsn.name, Edm, options, messageFunctions);
|
|
857
|
+
// distribute edm:Annotations into the schemas
|
|
858
|
+
// Distribute each anno into Schema
|
|
859
|
+
annos.forEach(anno => {
|
|
860
|
+
let targetSchema = whatsMySchemaName(anno.Target);
|
|
861
|
+
// if no target schema has been found, it's a service annotation that applies to the service schema
|
|
862
|
+
if(targetSchema === undefined)
|
|
863
|
+
targetSchema = serviceCsn.name;
|
|
864
|
+
if(targetSchema) {
|
|
865
|
+
if(targetSchema !== serviceCsn.name) {
|
|
866
|
+
anno.Target = anno.Target.replace(serviceCsn.name + '.', '');
|
|
867
|
+
}
|
|
868
|
+
edm._service._schemas[targetSchema]._annotations.push(anno);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
annos = [];
|
|
872
|
+
// add references for the used vocabularies
|
|
873
|
+
usedVocabularies.forEach(voc => {
|
|
874
|
+
let r = new Edm.Reference(v, voc.ref);
|
|
875
|
+
r.append(new Edm.Include(v, voc.inc))
|
|
876
|
+
edm._defaultRefs.push(r);
|
|
877
|
+
})
|
|
878
|
+
}
|
|
883
879
|
}
|
|
884
880
|
}
|
|
885
881
|
module.exports = { csn2edm, csn2edmAll };
|
package/lib/edm/edm.js
CHANGED
|
@@ -288,7 +288,8 @@ module.exports = function (options, error) {
|
|
|
288
288
|
});
|
|
289
289
|
let json_Annotations = Object.create(null);
|
|
290
290
|
this._annotations.filter(a => a.Target).forEach(a => json_Annotations[a.Target] = a.toJSON());
|
|
291
|
-
|
|
291
|
+
if(Object.keys(json_Annotations).length)
|
|
292
|
+
json['$Annotations'] = json_Annotations;
|
|
292
293
|
}
|
|
293
294
|
edmUtils.forAll(this._actions, (actionArray, actionName) => {
|
|
294
295
|
json[actionName] = [];
|
|
@@ -610,7 +611,12 @@ module.exports = function (options, error) {
|
|
|
610
611
|
if(scalarType) {
|
|
611
612
|
this[typeName] = csn._edmType;
|
|
612
613
|
// CDXCORE-CDXCORE-173 ignore type facets for Edm.Stream
|
|
613
|
-
|
|
614
|
+
// cds-compiler/issues/7835: Only set length for Binary as long as it is
|
|
615
|
+
// unclear how many bytes a string character represents.
|
|
616
|
+
// We can't calculate an unambiguous byte stream length for DB dependent
|
|
617
|
+
// multi-byte characters.
|
|
618
|
+
if(!(this[typeName] === 'Edm.Stream' &&
|
|
619
|
+
![ /*'cds.String',*/ 'cds.Binary'].includes(scalarType.type)))
|
|
614
620
|
edmUtils.addTypeFacets(this, scalarType);
|
|
615
621
|
}
|
|
616
622
|
else {
|
|
@@ -996,9 +1002,11 @@ module.exports = function (options, error) {
|
|
|
996
1002
|
// values are !null (with other words: a collection must never return [1,2,null,3])
|
|
997
1003
|
delete this.Nullable;
|
|
998
1004
|
}
|
|
999
|
-
|
|
1005
|
+
// we have exactly one selfReference or the default partner
|
|
1006
|
+
let partner = (!csn.$noPartner && csn._selfReferences.length === 1) ? csn._selfReferences[0] : csn._constraints._partnerCsn;
|
|
1000
1007
|
if(partner && partner['@odata.navigable'] !== false) {
|
|
1001
|
-
|
|
1008
|
+
// $abspath[0] is main entity
|
|
1009
|
+
this.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
|
|
1002
1010
|
}
|
|
1003
1011
|
|
|
1004
1012
|
/*
|
|
@@ -1084,13 +1092,6 @@ module.exports = function (options, error) {
|
|
|
1084
1092
|
return json;
|
|
1085
1093
|
}
|
|
1086
1094
|
|
|
1087
|
-
createNavigationPropertyBinding(namespace)
|
|
1088
|
-
{
|
|
1089
|
-
return new NavigationPropertyBinding(this._v,
|
|
1090
|
-
{ Path: this.Name, Target: this._csn._target.name.replace(namespace, '') }
|
|
1091
|
-
);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
1095
|
// V4 referential constraints!
|
|
1095
1096
|
addReferentialConstraintNodes()
|
|
1096
1097
|
{
|
|
@@ -1486,17 +1487,13 @@ module.exports = function (options, error) {
|
|
|
1486
1487
|
new End(v, { Role: fromRole[0], Type: fromRole[1], Multiplicity: multiplicity[0] } ),
|
|
1487
1488
|
new End(v, { Role: toRole[0], Type: toRole[1], Multiplicity: multiplicity[1] } ) );
|
|
1488
1489
|
|
|
1489
|
-
//
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
if(navProp._OnDelete)
|
|
1493
|
-
this._end[1].append(navProp._OnDelete);
|
|
1490
|
+
// set Delete:Cascade on composition end
|
|
1491
|
+
if(navProp._csn.type === 'cds.Composition')
|
|
1492
|
+
this._end[0].append(new OnDelete(v, { Action: 'Cascade' }));
|
|
1494
1493
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
this._end[0].append(new OnDelete(v, { Action: 'Cascade' } ) );
|
|
1499
|
-
}
|
|
1494
|
+
if(navProp._csn._selfReferences && navProp._csn._selfReferences.length &&
|
|
1495
|
+
navProp._csn._selfReferences[0].type === 'cds.Composition')
|
|
1496
|
+
this._end[1].append(new OnDelete(v, { Action: 'Cascade' }));
|
|
1500
1497
|
}
|
|
1501
1498
|
|
|
1502
1499
|
innerXML(indent)
|