@sap/cds-compiler 2.5.0 → 2.10.4
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 +191 -9
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +33 -3
- package/lib/api/main.js +29 -101
- package/lib/api/options.js +15 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +63 -9
- package/lib/base/messages.js +63 -21
- package/lib/base/model.js +2 -3
- 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 +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +16 -7
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +99 -42
- package/lib/compiler/index.js +73 -27
- package/lib/compiler/resolver.js +288 -157
- package/lib/compiler/shared.js +31 -11
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +103 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -114
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4713 -4279
- package/lib/json/from-csn.js +103 -45
- package/lib/json/to-csn.js +296 -117
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +21 -12
- package/lib/language/language.g4 +99 -31
- package/lib/main.d.ts +81 -3
- package/lib/main.js +30 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +329 -142
- package/lib/model/csnUtils.js +235 -58
- package/lib/model/enrichCsn.js +18 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +37 -20
- package/lib/optionProcessor.js +9 -3
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +112 -33
- package/lib/render/toHdbcds.js +134 -64
- package/lib/render/toSql.js +91 -38
- 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 +29 -13
- package/lib/transform/db/draft.js +8 -6
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +284 -63
- package/lib/transform/forHanaNew.js +98 -381
- package/lib/transform/forOdataNew.js +21 -22
- package/lib/transform/localized.js +37 -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 +134 -78
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- 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;
|
|
@@ -138,7 +142,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
138
142
|
definitions: Object.create(null)
|
|
139
143
|
}
|
|
140
144
|
};
|
|
141
|
-
|
|
145
|
+
|
|
142
146
|
if(options.isV4()) {
|
|
143
147
|
// tunnel schema xref and servicename in options to edm.Typebase to rectify
|
|
144
148
|
// type references that are eventually also prefixed with the service schema name.
|
|
@@ -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,8 +301,8 @@ 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
|
}
|
|
304
307
|
const Schema = new Edm.Schema(v, schema.name, undefined /* unset alias */, schema._csn, /* annotations */ [], schema.container);
|
|
305
308
|
const EntityContainer = Schema._ec || (LeadSchema && LeadSchema._ec);
|
|
@@ -307,7 +310,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
307
310
|
const schemaNamePrefix = schema.name + '.'
|
|
308
311
|
const schemaAliasPrefix = schemaNamePrefix;
|
|
309
312
|
const schemaCsn = schema;
|
|
310
|
-
|
|
313
|
+
const navigationProperties = [];
|
|
311
314
|
|
|
312
315
|
/* create the entitytypes and sets
|
|
313
316
|
Do not create an entity set if:
|
|
@@ -337,7 +340,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
337
340
|
}
|
|
338
341
|
|
|
339
342
|
// fetch all exising children names in a map
|
|
340
|
-
|
|
343
|
+
const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
|
|
341
344
|
if(acc[cur.Name] === undefined) {
|
|
342
345
|
acc[cur.Name] = [ cur ];
|
|
343
346
|
} else {
|
|
@@ -359,8 +362,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
359
362
|
|
|
360
363
|
// remove EntityContainer if empty
|
|
361
364
|
if(Schema._ec && Schema._ec._children.length === 0) {
|
|
362
|
-
|
|
363
|
-
Schema._children.splice(pos, 1);
|
|
365
|
+
Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
|
|
364
366
|
}
|
|
365
367
|
if(Schema._children.length === 0) {
|
|
366
368
|
// FIXME: Location for sub schemas?
|
|
@@ -369,8 +371,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
369
371
|
|
|
370
372
|
Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
|
|
371
373
|
if(refs.length > 1) {
|
|
372
|
-
|
|
373
|
-
error(null, ['definitions', artifactName], { name: Schema.Namespace },
|
|
374
|
+
error(null, ['definitions', `${Schema.Namespace}.${name}`], { name: Schema.Namespace },
|
|
374
375
|
'Duplicate name in Schema $(NAME)');
|
|
375
376
|
}
|
|
376
377
|
});
|
|
@@ -379,10 +380,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
379
380
|
|
|
380
381
|
function createEntityTypeAndSet(entityCsn)
|
|
381
382
|
{
|
|
382
|
-
|
|
383
|
-
|
|
383
|
+
const EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
|
|
384
|
+
const EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
|
|
384
385
|
|
|
385
|
-
|
|
386
|
+
const [ properties, hasStream ] = createProperties(entityCsn);
|
|
386
387
|
|
|
387
388
|
const loc = ['definitions', entityCsn.name];
|
|
388
389
|
const type = `${schema.name}.${EntityTypeName}`;
|
|
@@ -394,18 +395,18 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
394
395
|
properties.forEach(p => {
|
|
395
396
|
const pLoc = [...loc, 'elements', p.Name];
|
|
396
397
|
if(!p[p._typeName]) {
|
|
397
|
-
|
|
398
|
+
message('odata-spec-violation-type', pLoc);
|
|
398
399
|
}
|
|
399
400
|
if(p.Name === EntityTypeName) {
|
|
400
|
-
warning('odata-spec-violation-property-name', pLoc, {
|
|
401
|
+
warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
|
|
401
402
|
}
|
|
402
403
|
if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
|
|
403
|
-
warning('odata-spec-violation-array
|
|
404
|
+
warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
|
|
404
405
|
}
|
|
405
406
|
});
|
|
406
407
|
|
|
407
408
|
// construct EntityType attributes
|
|
408
|
-
|
|
409
|
+
const attributes = { Name : EntityTypeName };
|
|
409
410
|
|
|
410
411
|
// CDXCORE-CDXCORE-173
|
|
411
412
|
if(options.isV2() && hasStream)
|
|
@@ -418,7 +419,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
418
419
|
/** @type {object} */
|
|
419
420
|
let containerEntry;
|
|
420
421
|
|
|
421
|
-
if(isSingleton(entityCsn)) {
|
|
422
|
+
if(edmUtils.isSingleton(entityCsn, options.isV4())) {
|
|
422
423
|
containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
|
|
423
424
|
if(entityCsn['@odata.singleton.nullable'])
|
|
424
425
|
containerEntry.Nullable= true;
|
|
@@ -428,18 +429,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
428
429
|
}
|
|
429
430
|
|
|
430
431
|
// 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
|
-
|
|
432
|
+
if(options.isV4()) {
|
|
433
|
+
entityCsn.$edmNPBs.forEach(npb => {
|
|
434
|
+
containerEntry.append(new Edm.NavigationPropertyBinding(v, npb))
|
|
435
|
+
});
|
|
436
|
+
}
|
|
443
437
|
EntityContainer.register(containerEntry);
|
|
444
438
|
}
|
|
445
439
|
|
|
@@ -448,38 +442,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
448
442
|
(options.isV4()) ? createActionV4(a, n, entityCsn)
|
|
449
443
|
: createActionV2(a, n, entityCsn)
|
|
450
444
|
});
|
|
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
445
|
}
|
|
458
446
|
|
|
459
447
|
// add bound/unbound actions/functions for V4
|
|
460
448
|
function createActionV4(actionCsn, name, entityCsn=undefined)
|
|
461
449
|
{
|
|
462
|
-
|
|
450
|
+
const iAmAnAction = actionCsn.kind === 'action';
|
|
463
451
|
|
|
464
|
-
|
|
452
|
+
const actionName = edmUtils.getBaseName(actionCsn.name);
|
|
465
453
|
|
|
466
|
-
|
|
454
|
+
const attributes = { Name: actionName, IsBound : false };
|
|
467
455
|
|
|
468
456
|
if(!iAmAnAction)
|
|
469
457
|
attributes.IsComposable = false;
|
|
470
458
|
|
|
471
459
|
/** @type {object} */
|
|
472
|
-
|
|
460
|
+
const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
|
|
473
461
|
: new Edm.FunctionDefinition(v, attributes);
|
|
474
462
|
|
|
475
463
|
// bpName is eventually used later for EntitySetPath
|
|
476
|
-
|
|
477
|
-
|
|
464
|
+
const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
|
|
465
|
+
const bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
|
|
478
466
|
|
|
479
467
|
if(entityCsn != undefined)
|
|
480
468
|
{
|
|
481
469
|
actionNode.IsBound = true;
|
|
482
|
-
|
|
470
|
+
const bpType = fullQualified(entityCsn.name);
|
|
483
471
|
// Binding Parameter: 'in' at first position in sequence, this is decisive!
|
|
484
472
|
if(actionCsn['@cds.odata.bindingparameter.collection'])
|
|
485
473
|
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true } ));
|
|
@@ -489,14 +477,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
489
477
|
else if(EntityContainer)// unbound => produce Action/FunctionImport
|
|
490
478
|
{
|
|
491
479
|
/** @type {object} */
|
|
492
|
-
|
|
480
|
+
const actionImport = iAmAnAction
|
|
493
481
|
? new Edm.ActionImport(v, { Name: actionName, Action : fullQualified(actionName) })
|
|
494
482
|
: new Edm.FunctionImport(v, { Name: actionName, Function : fullQualified(actionName) });
|
|
495
483
|
|
|
496
|
-
|
|
484
|
+
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
497
485
|
if(rt) // add EntitySet attribute only if return type is a non abstract entity
|
|
498
486
|
{
|
|
499
|
-
|
|
487
|
+
const definition = schemaCsn.definitions[rt];
|
|
500
488
|
if(definition && definition.kind === 'entity' && !definition.abstract)
|
|
501
489
|
{
|
|
502
490
|
actionImport.EntitySet = edmUtils.getBaseName(rt);
|
|
@@ -525,7 +513,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
525
513
|
function createActionV2(actionCsn, name, entityCsn=undefined)
|
|
526
514
|
{
|
|
527
515
|
/** @type {object} */
|
|
528
|
-
|
|
516
|
+
const functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
|
|
529
517
|
|
|
530
518
|
// inserted now to maintain attribute order with old odata generator...
|
|
531
519
|
/*
|
|
@@ -539,10 +527,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
539
527
|
*/
|
|
540
528
|
|
|
541
529
|
const actLoc = ['definitions', ...(entityCsn ? [entityCsn.name, 'actions', actionCsn.name] : [actionCsn.name])];
|
|
542
|
-
|
|
530
|
+
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
543
531
|
if(rt) // add EntitySet attribute only if return type is an entity
|
|
544
532
|
{
|
|
545
|
-
|
|
533
|
+
const defintion = schemaCsn.definitions[rt];
|
|
546
534
|
if(defintion && edmUtils.isEntity(defintion))
|
|
547
535
|
{
|
|
548
536
|
functionImport.EntitySet = rt.replace(schemaNamePrefix, '');
|
|
@@ -586,10 +574,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
586
574
|
const paramLoc = [...actLoc, 'params', parameterName];
|
|
587
575
|
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
588
576
|
if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
|
|
589
|
-
warning('odata-spec-violation-param
|
|
577
|
+
warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
|
|
590
578
|
}
|
|
591
579
|
if(param._isCollection) {
|
|
592
|
-
warning('odata-spec-violation-array
|
|
580
|
+
warning('odata-spec-violation-array', paramLoc, { api: 'OData V2' });
|
|
593
581
|
}
|
|
594
582
|
functionImport.append(param);
|
|
595
583
|
});
|
|
@@ -600,12 +588,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
600
588
|
function getReturnType(action)
|
|
601
589
|
{
|
|
602
590
|
// it is safe to assume that either type or items.type are set
|
|
603
|
-
|
|
591
|
+
const returns = action.returns.items || action.returns;
|
|
604
592
|
let type = returns.type;
|
|
605
593
|
if(type){
|
|
606
594
|
if(!isBuiltinType(type) && !['entity', 'view', 'type'].includes(csn.definitions[type].kind)){
|
|
607
595
|
const returnsLoc = [ ...actLoc, 'returns'];
|
|
608
|
-
warning('odata-spec-violation-returns
|
|
596
|
+
warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
|
|
609
597
|
}
|
|
610
598
|
type = edmUtils.mapCdsToEdmType(returns, messageFunctions, options.isV2());
|
|
611
599
|
}
|
|
@@ -618,19 +606,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
618
606
|
}
|
|
619
607
|
|
|
620
608
|
/**
|
|
621
|
-
* @param {object}
|
|
609
|
+
* @param {object} elementsCsn
|
|
610
|
+
* @param {object} edmParentCsn
|
|
622
611
|
* @returns {[object[], boolean]} Returns a [ [ Edm Properties ], boolean hasStream ]:
|
|
623
612
|
* array of Edm Properties
|
|
624
613
|
* boolean hasStream : true if at least one element has @Core.MediaType assignment
|
|
625
614
|
*/
|
|
626
|
-
function createProperties(
|
|
615
|
+
function createProperties(elementsCsn, edmParentCsn=elementsCsn)
|
|
627
616
|
{
|
|
628
|
-
|
|
617
|
+
const props = [];
|
|
629
618
|
let hasStream = false;
|
|
630
|
-
edmUtils.forAll(
|
|
619
|
+
edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
|
|
631
620
|
{
|
|
632
|
-
if(elementCsn.
|
|
633
|
-
setProp(elementCsn, '
|
|
621
|
+
if(elementCsn._edmParentCsn == undefined)
|
|
622
|
+
setProp(elementCsn, '_edmParentCsn', edmParentCsn);
|
|
634
623
|
|
|
635
624
|
if(!elementCsn._ignore) {
|
|
636
625
|
if(edmUtils.isAssociationOrComposition(elementCsn))
|
|
@@ -644,11 +633,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
644
633
|
// (undefined !== false) still evaluates to true
|
|
645
634
|
if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
|
|
646
635
|
{
|
|
647
|
-
|
|
636
|
+
const navProp = new Edm.NavigationProperty(v, {
|
|
648
637
|
Name: elementName,
|
|
649
638
|
Type: elementCsn._target.name
|
|
650
639
|
}, elementCsn);
|
|
651
|
-
|
|
652
640
|
props.push(navProp);
|
|
653
641
|
// save the navProp in the global array for late constraint building
|
|
654
642
|
navigationProperties.push(navProp);
|
|
@@ -667,7 +655,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
667
655
|
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
668
656
|
// V4: render property type 'Edm.Stream'
|
|
669
657
|
hasStream = true;
|
|
670
|
-
info(null, ['definitions',
|
|
658
|
+
info(null, ['definitions', elementsCsn.name], { name: elementsCsn.name, id: elementName, anno: '@Core.MediaType' },
|
|
671
659
|
'$(NAME): Property $(ID) annotated with $(ANNO) is removed from EDM in OData V2');
|
|
672
660
|
|
|
673
661
|
} else {
|
|
@@ -683,11 +671,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
683
671
|
function createComplexType(structuredTypeCsn)
|
|
684
672
|
{
|
|
685
673
|
// V4 attributes: Name, BaseType, Abstract, OpenType
|
|
686
|
-
|
|
674
|
+
const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
|
|
687
675
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
676
|
+
const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
|
|
677
|
+
const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
|
|
678
|
+
const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
|
|
691
679
|
const loc = ['definitions', structuredTypeCsn.name];
|
|
692
680
|
|
|
693
681
|
if(properties.length === 0) {
|
|
@@ -697,17 +685,17 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
697
685
|
properties.forEach(p => {
|
|
698
686
|
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p.Name ];
|
|
699
687
|
if(!p[p._typeName]) {
|
|
700
|
-
|
|
688
|
+
message('odata-spec-violation-type', pLoc);
|
|
701
689
|
}
|
|
702
690
|
if(p.Name === complexType.Name) {
|
|
703
|
-
warning('odata-spec-violation-property-name', pLoc, {
|
|
691
|
+
warning('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
|
|
704
692
|
}
|
|
705
693
|
if(options.isV2()) {
|
|
706
694
|
if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
|
|
707
|
-
warning('odata-spec-violation-array
|
|
695
|
+
warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
|
|
708
696
|
}
|
|
709
697
|
if(edmUtils.isAssociationOrComposition(p._csn)) {
|
|
710
|
-
warning('odata-spec-violation-assoc
|
|
698
|
+
warning('odata-spec-violation-assoc', pLoc, { api: 'OData V2' });
|
|
711
699
|
}
|
|
712
700
|
}
|
|
713
701
|
});
|
|
@@ -722,7 +710,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
722
710
|
function createTypeDefinition(typeCsn)
|
|
723
711
|
{
|
|
724
712
|
// derived types are already resolved to base types
|
|
725
|
-
|
|
713
|
+
const props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
|
|
726
714
|
const typeDef = new Edm.TypeDefinition(v, props, typeCsn );
|
|
727
715
|
Schema.append(typeDef);
|
|
728
716
|
}
|
|
@@ -744,7 +732,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
744
732
|
function addAssociation(navigationProperty)
|
|
745
733
|
{
|
|
746
734
|
let constraints = navigationProperty._csn._constraints;
|
|
747
|
-
let parentName = navigationProperty._csn.
|
|
735
|
+
let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
748
736
|
let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
749
737
|
let assocName = plainAssocName;
|
|
750
738
|
let i = 1;
|
|
@@ -760,7 +748,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
760
748
|
|
|
761
749
|
// The entity set name may not be the same as the type name (parameterized entities have
|
|
762
750
|
// differing set names (<T>Parameters => <T>, <T>Type => <T>Set)
|
|
763
|
-
let fromEntitySet = ( navigationProperty._csn.
|
|
751
|
+
let fromEntitySet = ( navigationProperty._csn._edmParentCsn.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
|
|
764
752
|
let toEntitySet = (navigationProperty._targetCsn.$entitySetName || toEntityType).replace(schemaNamePrefix, '');
|
|
765
753
|
|
|
766
754
|
// from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })
|
|
@@ -802,7 +790,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
802
790
|
[ fromEntityType, toEntityType ] = [ toEntityType, fromEntityType ];
|
|
803
791
|
[ fromEntitySet, toEntitySet ] = [ toEntitySet, fromEntitySet ];
|
|
804
792
|
|
|
805
|
-
parentName = forwardAssocCsn.
|
|
793
|
+
parentName = forwardAssocCsn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
806
794
|
assocName = plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
807
795
|
i = 1;
|
|
808
796
|
while(NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association)) {
|
|
@@ -817,56 +805,37 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
817
805
|
}
|
|
818
806
|
|
|
819
807
|
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
808
|
return;
|
|
839
|
-
}
|
|
840
809
|
|
|
841
810
|
// Create Association and AssociationSet if this is not a backlink association.
|
|
842
811
|
// Store association at navigation property because in case the Ends must be modified
|
|
843
812
|
// later by the partner (backlink) association
|
|
844
|
-
|
|
813
|
+
const edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
|
|
845
814
|
[ fromRole, fullQualified(fromEntityType) ],
|
|
846
815
|
[ toRole, fullQualified(toEntityType) ],
|
|
847
816
|
constraints._multiplicity );
|
|
848
817
|
if(NamesInSchemaXRef[assocName] === undefined) {
|
|
849
|
-
NamesInSchemaXRef[assocName] = [
|
|
818
|
+
NamesInSchemaXRef[assocName] = [ edmAssociation ];
|
|
850
819
|
}
|
|
851
820
|
else {
|
|
852
|
-
|
|
821
|
+
NamesInSchemaXRef[assocName].push(edmAssociation);
|
|
853
822
|
}
|
|
854
823
|
// Add ReferentialConstraints if any
|
|
855
824
|
if(!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
|
|
856
825
|
// A managed composition is treated as association
|
|
857
826
|
if(navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
|
|
858
|
-
|
|
827
|
+
edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
|
|
859
828
|
toRole, fromRole, constraints.constraints));
|
|
860
829
|
}
|
|
861
830
|
else {
|
|
862
|
-
|
|
831
|
+
edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
|
|
863
832
|
fromRole, toRole, constraints.constraints));
|
|
864
833
|
}
|
|
865
834
|
}
|
|
866
835
|
|
|
867
|
-
Schema.append(
|
|
836
|
+
Schema.append(edmAssociation);
|
|
868
837
|
if(EntityContainer && !navigationProperty._targetCsn.$proxy) {
|
|
869
|
-
|
|
838
|
+
const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
|
|
870
839
|
fromRole, toRole, fromEntitySet, toEntitySet);
|
|
871
840
|
if(navigationProperty._csn._SetAttributes)
|
|
872
841
|
assocSet.setSapVocabularyAsAttributes(navigationProperty._csn._SetAttributes);
|
|
@@ -880,6 +849,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
880
849
|
return schemaAliasPrefix + name.replace(schemaNamePrefix, '')
|
|
881
850
|
}
|
|
882
851
|
}
|
|
852
|
+
|
|
853
|
+
// generate the Edm.Annotations tree and append it to the corresponding schema
|
|
854
|
+
function addAnnotations() {
|
|
855
|
+
let { annos, usedVocabularies } = translate.csn2annotationEdm(csn, serviceCsn.name, Edm, options, messageFunctions);
|
|
856
|
+
// distribute edm:Annotations into the schemas
|
|
857
|
+
// Distribute each anno into Schema
|
|
858
|
+
annos.forEach(anno => {
|
|
859
|
+
let targetSchema = whatsMySchemaName(anno.Target);
|
|
860
|
+
// if no target schema has been found, it's a service annotation that applies to the service schema
|
|
861
|
+
if(targetSchema === undefined)
|
|
862
|
+
targetSchema = serviceCsn.name;
|
|
863
|
+
if(targetSchema) {
|
|
864
|
+
if(targetSchema !== serviceCsn.name) {
|
|
865
|
+
anno.Target = anno.Target.replace(serviceCsn.name + '.', '');
|
|
866
|
+
}
|
|
867
|
+
edm._service._schemas[targetSchema]._annotations.push(anno);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
annos = [];
|
|
871
|
+
// add references for the used vocabularies
|
|
872
|
+
usedVocabularies.forEach(voc => {
|
|
873
|
+
let r = new Edm.Reference(v, voc.ref);
|
|
874
|
+
r.append(new Edm.Include(v, voc.inc))
|
|
875
|
+
edm._defaultRefs.push(r);
|
|
876
|
+
})
|
|
877
|
+
}
|
|
883
878
|
}
|
|
884
879
|
}
|
|
885
880
|
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)
|