@sap/cds-compiler 2.4.4 → 2.10.2
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 +241 -1
- package/bin/.eslintrc.json +17 -0
- package/bin/cds_update_identifiers.js +8 -7
- package/bin/cdsc.js +180 -132
- package/bin/cdshi.js +18 -11
- package/bin/cdsse.js +38 -32
- package/bin/cdsv2m.js +8 -7
- package/doc/CHANGELOG_BETA.md +36 -1
- package/lib/api/main.js +81 -100
- package/lib/api/options.js +17 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/location.js +2 -2
- package/lib/base/message-registry.js +66 -4
- package/lib/base/messages.js +84 -27
- package/lib/base/model.js +2 -61
- package/lib/checks/arrayOfs.js +0 -1
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/enricher.js +8 -2
- 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 +27 -9
- 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 +66 -13
- package/lib/compiler/assert-consistency.js +24 -12
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +101 -39
- package/lib/compiler/index.js +88 -59
- package/lib/compiler/resolver.js +455 -209
- package/lib/compiler/shared.js +57 -33
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +128 -99
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -127
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +74 -28
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +18 -4
- package/lib/gen/language.tokens +124 -118
- package/lib/gen/languageLexer.interp +13 -1
- package/lib/gen/languageLexer.js +870 -839
- package/lib/gen/languageLexer.tokens +116 -111
- package/lib/gen/languageParser.js +5894 -5614
- package/lib/json/from-csn.js +152 -67
- package/lib/json/to-csn.js +334 -135
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +24 -14
- package/lib/language/language.g4 +188 -128
- package/lib/main.d.ts +435 -0
- package/lib/main.js +31 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +463 -187
- package/lib/model/csnUtils.js +280 -136
- package/lib/model/enrichCsn.js +75 -4
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +70 -25
- package/lib/optionProcessor.js +13 -10
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +123 -40
- package/lib/render/toHdbcds.js +156 -65
- package/lib/render/toSql.js +87 -11
- package/lib/render/utils/common.js +55 -9
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/{sql → db}/.eslintrc.json +0 -0
- package/lib/transform/{sql → db}/assertUnique.js +7 -8
- package/lib/transform/{sql → db}/constraints.js +35 -20
- package/lib/transform/db/draft.js +353 -0
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
- package/lib/transform/{sql → db}/helpers.js +0 -0
- package/lib/transform/{sql → db}/transformExists.js +256 -60
- package/lib/transform/forHanaNew.js +216 -765
- package/lib/transform/forOdataNew.js +60 -56
- package/lib/transform/localized.js +48 -26
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
- package/lib/transform/odata/generateForeignKeyElements.js +13 -12
- package/lib/transform/odata/referenceFlattener.js +60 -36
- package/lib/transform/odata/sortByAssociationDependency.js +4 -4
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +21 -22
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +27 -17
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +141 -77
- 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/lib/utils/timetrace.js +6 -1
- package/package.json +2 -1
- package/lib/base/deepCopy.js +0 -66
- package/lib/json/walker.js +0 -26
- package/lib/utils/string.js +0 -17
package/lib/edm/csn2edm.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
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');
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
12
|
const { setProp } = require('../base/model');
|
|
13
|
-
const { cloneCsn, isEdmPropertyRendered } = require('../model/csnUtils');
|
|
13
|
+
const { cloneCsn, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
|
|
14
14
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
15
15
|
const { makeMessageFunction } = require('../base/messages');
|
|
16
16
|
|
|
@@ -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}`;
|
|
@@ -392,16 +393,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
392
393
|
warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no primary key');
|
|
393
394
|
}
|
|
394
395
|
properties.forEach(p => {
|
|
396
|
+
const pLoc = [...loc, 'elements', p.Name];
|
|
395
397
|
if(!p[p._typeName]) {
|
|
396
|
-
|
|
398
|
+
message('odata-spec-violation-type', pLoc);
|
|
397
399
|
}
|
|
398
400
|
if(p.Name === EntityTypeName) {
|
|
399
|
-
warning('odata-spec-violation-property-name',
|
|
401
|
+
warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
|
|
402
|
+
}
|
|
403
|
+
if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
|
|
404
|
+
warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
|
|
400
405
|
}
|
|
401
406
|
});
|
|
402
407
|
|
|
403
408
|
// construct EntityType attributes
|
|
404
|
-
|
|
409
|
+
const attributes = { Name : EntityTypeName };
|
|
405
410
|
|
|
406
411
|
// CDXCORE-CDXCORE-173
|
|
407
412
|
if(options.isV2() && hasStream)
|
|
@@ -413,11 +418,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
413
418
|
{
|
|
414
419
|
/** @type {object} */
|
|
415
420
|
let containerEntry;
|
|
416
|
-
let singleton = entityCsn['@odata.singleton'];
|
|
417
|
-
let hasNullable = entityCsn['@odata.singleton.nullable'] !== undefined &&
|
|
418
|
-
entityCsn['@odata.singleton.nullable'] !== null;
|
|
419
421
|
|
|
420
|
-
if(
|
|
422
|
+
if(edmUtils.isSingleton(entityCsn, options.isV4())) {
|
|
421
423
|
containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
|
|
422
424
|
if(entityCsn['@odata.singleton.nullable'])
|
|
423
425
|
containerEntry.Nullable= true;
|
|
@@ -427,15 +429,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
427
429
|
}
|
|
428
430
|
|
|
429
431
|
// V4: Create NavigationPropertyBinding in EntitySet
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
!np.isContainment() && !edmUtils.isContainee(np._targetCsn) && !np._targetCsn.$proxy && !np._targetCsn.$externalRef
|
|
436
|
-
). forEach(np =>
|
|
437
|
-
containerEntry.append(np.createNavigationPropertyBinding(schemaNamePrefix)));
|
|
438
|
-
|
|
432
|
+
if(options.isV4()) {
|
|
433
|
+
entityCsn.$edmNPBs.forEach(npb => {
|
|
434
|
+
containerEntry.append(new Edm.NavigationPropertyBinding(v, npb))
|
|
435
|
+
});
|
|
436
|
+
}
|
|
439
437
|
EntityContainer.register(containerEntry);
|
|
440
438
|
}
|
|
441
439
|
|
|
@@ -449,27 +447,27 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
449
447
|
// add bound/unbound actions/functions for V4
|
|
450
448
|
function createActionV4(actionCsn, name, entityCsn=undefined)
|
|
451
449
|
{
|
|
452
|
-
|
|
450
|
+
const iAmAnAction = actionCsn.kind === 'action';
|
|
453
451
|
|
|
454
|
-
|
|
452
|
+
const actionName = edmUtils.getBaseName(actionCsn.name);
|
|
455
453
|
|
|
456
|
-
|
|
454
|
+
const attributes = { Name: actionName, IsBound : false };
|
|
457
455
|
|
|
458
456
|
if(!iAmAnAction)
|
|
459
457
|
attributes.IsComposable = false;
|
|
460
458
|
|
|
461
459
|
/** @type {object} */
|
|
462
|
-
|
|
460
|
+
const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
|
|
463
461
|
: new Edm.FunctionDefinition(v, attributes);
|
|
464
462
|
|
|
465
463
|
// bpName is eventually used later for EntitySetPath
|
|
466
|
-
|
|
467
|
-
|
|
464
|
+
const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
|
|
465
|
+
const bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
|
|
468
466
|
|
|
469
467
|
if(entityCsn != undefined)
|
|
470
468
|
{
|
|
471
469
|
actionNode.IsBound = true;
|
|
472
|
-
|
|
470
|
+
const bpType = fullQualified(entityCsn.name);
|
|
473
471
|
// Binding Parameter: 'in' at first position in sequence, this is decisive!
|
|
474
472
|
if(actionCsn['@cds.odata.bindingparameter.collection'])
|
|
475
473
|
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true } ));
|
|
@@ -479,14 +477,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
479
477
|
else if(EntityContainer)// unbound => produce Action/FunctionImport
|
|
480
478
|
{
|
|
481
479
|
/** @type {object} */
|
|
482
|
-
|
|
480
|
+
const actionImport = iAmAnAction
|
|
483
481
|
? new Edm.ActionImport(v, { Name: actionName, Action : fullQualified(actionName) })
|
|
484
482
|
: new Edm.FunctionImport(v, { Name: actionName, Function : fullQualified(actionName) });
|
|
485
483
|
|
|
486
|
-
|
|
484
|
+
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
487
485
|
if(rt) // add EntitySet attribute only if return type is a non abstract entity
|
|
488
486
|
{
|
|
489
|
-
|
|
487
|
+
const definition = schemaCsn.definitions[rt];
|
|
490
488
|
if(definition && definition.kind === 'entity' && !definition.abstract)
|
|
491
489
|
{
|
|
492
490
|
actionImport.EntitySet = edmUtils.getBaseName(rt);
|
|
@@ -515,7 +513,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
515
513
|
function createActionV2(actionCsn, name, entityCsn=undefined)
|
|
516
514
|
{
|
|
517
515
|
/** @type {object} */
|
|
518
|
-
|
|
516
|
+
const functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
|
|
519
517
|
|
|
520
518
|
// inserted now to maintain attribute order with old odata generator...
|
|
521
519
|
/*
|
|
@@ -528,10 +526,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
528
526
|
in the spec and advised mention it as in V4
|
|
529
527
|
*/
|
|
530
528
|
|
|
531
|
-
|
|
529
|
+
const actLoc = ['definitions', ...(entityCsn ? [entityCsn.name, 'actions', actionCsn.name] : [actionCsn.name])];
|
|
530
|
+
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
532
531
|
if(rt) // add EntitySet attribute only if return type is an entity
|
|
533
532
|
{
|
|
534
|
-
|
|
533
|
+
const defintion = schemaCsn.definitions[rt];
|
|
535
534
|
if(defintion && edmUtils.isEntity(defintion))
|
|
536
535
|
{
|
|
537
536
|
functionImport.EntitySet = rt.replace(schemaNamePrefix, '');
|
|
@@ -572,7 +571,15 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
572
571
|
// V2 XML spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
|
|
573
572
|
// the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
|
|
574
573
|
edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
|
|
575
|
-
|
|
574
|
+
const paramLoc = [...actLoc, 'params', parameterName];
|
|
575
|
+
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
576
|
+
if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
|
|
577
|
+
warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
|
|
578
|
+
}
|
|
579
|
+
if(param._isCollection) {
|
|
580
|
+
warning('odata-spec-violation-array', paramLoc, { api: 'OData V2' });
|
|
581
|
+
}
|
|
582
|
+
functionImport.append(param);
|
|
576
583
|
});
|
|
577
584
|
|
|
578
585
|
if(EntityContainer)
|
|
@@ -581,10 +588,15 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
581
588
|
function getReturnType(action)
|
|
582
589
|
{
|
|
583
590
|
// it is safe to assume that either type or items.type are set
|
|
584
|
-
|
|
591
|
+
const returns = action.returns.items || action.returns;
|
|
585
592
|
let type = returns.type;
|
|
586
|
-
if(type)
|
|
593
|
+
if(type){
|
|
594
|
+
if(!isBuiltinType(type) && !['entity', 'view', 'type'].includes(csn.definitions[type].kind)){
|
|
595
|
+
const returnsLoc = [ ...actLoc, 'returns'];
|
|
596
|
+
warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
|
|
597
|
+
}
|
|
587
598
|
type = edmUtils.mapCdsToEdmType(returns, messageFunctions, options.isV2());
|
|
599
|
+
}
|
|
588
600
|
|
|
589
601
|
if(action.returns._isCollection)
|
|
590
602
|
type = `Collection(${type})`
|
|
@@ -594,19 +606,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
594
606
|
}
|
|
595
607
|
|
|
596
608
|
/**
|
|
597
|
-
* @param {object}
|
|
609
|
+
* @param {object} elementsCsn
|
|
610
|
+
* @param {object} edmParentCsn
|
|
598
611
|
* @returns {[object[], boolean]} Returns a [ [ Edm Properties ], boolean hasStream ]:
|
|
599
612
|
* array of Edm Properties
|
|
600
613
|
* boolean hasStream : true if at least one element has @Core.MediaType assignment
|
|
601
614
|
*/
|
|
602
|
-
function createProperties(
|
|
615
|
+
function createProperties(elementsCsn, edmParentCsn=elementsCsn)
|
|
603
616
|
{
|
|
604
|
-
|
|
617
|
+
const props = [];
|
|
605
618
|
let hasStream = false;
|
|
606
|
-
edmUtils.forAll(
|
|
619
|
+
edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
|
|
607
620
|
{
|
|
608
|
-
if(elementCsn.
|
|
609
|
-
setProp(elementCsn, '
|
|
621
|
+
if(elementCsn._edmParentCsn == undefined)
|
|
622
|
+
setProp(elementCsn, '_edmParentCsn', edmParentCsn);
|
|
610
623
|
|
|
611
624
|
if(!elementCsn._ignore) {
|
|
612
625
|
if(edmUtils.isAssociationOrComposition(elementCsn))
|
|
@@ -620,11 +633,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
620
633
|
// (undefined !== false) still evaluates to true
|
|
621
634
|
if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
|
|
622
635
|
{
|
|
623
|
-
|
|
636
|
+
const navProp = new Edm.NavigationProperty(v, {
|
|
624
637
|
Name: elementName,
|
|
625
638
|
Type: elementCsn._target.name
|
|
626
639
|
}, elementCsn);
|
|
627
|
-
|
|
628
640
|
props.push(navProp);
|
|
629
641
|
// save the navProp in the global array for late constraint building
|
|
630
642
|
navigationProperties.push(navProp);
|
|
@@ -643,7 +655,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
643
655
|
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
644
656
|
// V4: render property type 'Edm.Stream'
|
|
645
657
|
hasStream = true;
|
|
646
|
-
info(null, ['definitions',
|
|
658
|
+
info(null, ['definitions', elementsCsn.name], { name: elementsCsn.name, id: elementName, anno: '@Core.MediaType' },
|
|
647
659
|
'$(NAME): Property $(ID) annotated with $(ANNO) is removed from EDM in OData V2');
|
|
648
660
|
|
|
649
661
|
} else {
|
|
@@ -659,22 +671,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
659
671
|
function createComplexType(structuredTypeCsn)
|
|
660
672
|
{
|
|
661
673
|
// V4 attributes: Name, BaseType, Abstract, OpenType
|
|
662
|
-
|
|
674
|
+
const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
|
|
663
675
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
676
|
+
const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
|
|
677
|
+
const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
|
|
678
|
+
const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
|
|
679
|
+
const loc = ['definitions', structuredTypeCsn.name];
|
|
667
680
|
|
|
668
681
|
if(properties.length === 0) {
|
|
669
682
|
warning(null, ['definitions', structuredTypeCsn.name], { name: structuredTypeCsn.name },
|
|
670
683
|
'EDM ComplexType $(NAME) has no properties');
|
|
671
684
|
}
|
|
672
685
|
properties.forEach(p => {
|
|
686
|
+
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p.Name ];
|
|
673
687
|
if(!p[p._typeName]) {
|
|
674
|
-
|
|
688
|
+
message('odata-spec-violation-type', pLoc);
|
|
675
689
|
}
|
|
676
690
|
if(p.Name === complexType.Name) {
|
|
677
|
-
warning('odata-spec-violation-property-name',
|
|
691
|
+
warning('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
|
|
692
|
+
}
|
|
693
|
+
if(options.isV2()) {
|
|
694
|
+
if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
|
|
695
|
+
warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
|
|
696
|
+
}
|
|
697
|
+
if(edmUtils.isAssociationOrComposition(p._csn)) {
|
|
698
|
+
warning('odata-spec-violation-assoc', pLoc, { api: 'OData V2' });
|
|
699
|
+
}
|
|
678
700
|
}
|
|
679
701
|
});
|
|
680
702
|
|
|
@@ -688,7 +710,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
688
710
|
function createTypeDefinition(typeCsn)
|
|
689
711
|
{
|
|
690
712
|
// derived types are already resolved to base types
|
|
691
|
-
|
|
713
|
+
const props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
|
|
692
714
|
const typeDef = new Edm.TypeDefinition(v, props, typeCsn );
|
|
693
715
|
Schema.append(typeDef);
|
|
694
716
|
}
|
|
@@ -710,7 +732,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
710
732
|
function addAssociation(navigationProperty)
|
|
711
733
|
{
|
|
712
734
|
let constraints = navigationProperty._csn._constraints;
|
|
713
|
-
let parentName = navigationProperty._csn.
|
|
735
|
+
let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
714
736
|
let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
715
737
|
let assocName = plainAssocName;
|
|
716
738
|
let i = 1;
|
|
@@ -726,7 +748,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
726
748
|
|
|
727
749
|
// The entity set name may not be the same as the type name (parameterized entities have
|
|
728
750
|
// differing set names (<T>Parameters => <T>, <T>Type => <T>Set)
|
|
729
|
-
let fromEntitySet = ( navigationProperty._csn.
|
|
751
|
+
let fromEntitySet = ( navigationProperty._csn._edmParentCsn.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
|
|
730
752
|
let toEntitySet = (navigationProperty._targetCsn.$entitySetName || toEntityType).replace(schemaNamePrefix, '');
|
|
731
753
|
|
|
732
754
|
// from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })
|
|
@@ -768,7 +790,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
768
790
|
[ fromEntityType, toEntityType ] = [ toEntityType, fromEntityType ];
|
|
769
791
|
[ fromEntitySet, toEntitySet ] = [ toEntitySet, fromEntitySet ];
|
|
770
792
|
|
|
771
|
-
parentName = forwardAssocCsn.
|
|
793
|
+
parentName = forwardAssocCsn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
772
794
|
assocName = plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
773
795
|
i = 1;
|
|
774
796
|
while(NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association)) {
|
|
@@ -783,56 +805,37 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
783
805
|
}
|
|
784
806
|
|
|
785
807
|
if(reuseAssoc)
|
|
786
|
-
{
|
|
787
|
-
// Example:
|
|
788
|
-
// entity E { key id: Integer; toF: association to F; };
|
|
789
|
-
// entity F { key id: Integer; toE: composition of E on toE.toF = $self; };
|
|
790
|
-
//
|
|
791
|
-
// Consider we're in NavigationProperty 'toE' which is the backlink to F.
|
|
792
|
-
// Then forwardAssocCsn is 'E_toF' with two Ends: E, F.
|
|
793
|
-
// Backlink F.toE is a composition, making E existentially dependant on F.
|
|
794
|
-
// So End E of Association E_toF (which is End[0]) receives Edm.OnDelete.
|
|
795
|
-
// Depending on the order of the navigation properties it might be that the
|
|
796
|
-
// forward Edm.Association has not yet been produced. In this case Edm.OnDelete
|
|
797
|
-
// is parked at the forward NavigationProperty.
|
|
798
|
-
|
|
799
|
-
if(!forwardAssocCsn._NavigationProperty._edmAssociation && navigationProperty._csn.type === 'cds.Composition')
|
|
800
|
-
{
|
|
801
|
-
// TODO: to be specified via @sap.on.delete
|
|
802
|
-
forwardAssocCsn._NavigationProperty.set( { _OnDelete: new Edm.OnDelete(v, { Action: 'Cascade' }) } );
|
|
803
|
-
}
|
|
804
808
|
return;
|
|
805
|
-
}
|
|
806
809
|
|
|
807
810
|
// Create Association and AssociationSet if this is not a backlink association.
|
|
808
811
|
// Store association at navigation property because in case the Ends must be modified
|
|
809
812
|
// later by the partner (backlink) association
|
|
810
|
-
|
|
813
|
+
const edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
|
|
811
814
|
[ fromRole, fullQualified(fromEntityType) ],
|
|
812
815
|
[ toRole, fullQualified(toEntityType) ],
|
|
813
816
|
constraints._multiplicity );
|
|
814
817
|
if(NamesInSchemaXRef[assocName] === undefined) {
|
|
815
|
-
NamesInSchemaXRef[assocName] = [
|
|
818
|
+
NamesInSchemaXRef[assocName] = [ edmAssociation ];
|
|
816
819
|
}
|
|
817
820
|
else {
|
|
818
|
-
|
|
821
|
+
NamesInSchemaXRef[assocName].push(edmAssociation);
|
|
819
822
|
}
|
|
820
823
|
// Add ReferentialConstraints if any
|
|
821
824
|
if(!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
|
|
822
825
|
// A managed composition is treated as association
|
|
823
826
|
if(navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
|
|
824
|
-
|
|
827
|
+
edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
|
|
825
828
|
toRole, fromRole, constraints.constraints));
|
|
826
829
|
}
|
|
827
830
|
else {
|
|
828
|
-
|
|
831
|
+
edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
|
|
829
832
|
fromRole, toRole, constraints.constraints));
|
|
830
833
|
}
|
|
831
834
|
}
|
|
832
835
|
|
|
833
|
-
Schema.append(
|
|
836
|
+
Schema.append(edmAssociation);
|
|
834
837
|
if(EntityContainer && !navigationProperty._targetCsn.$proxy) {
|
|
835
|
-
|
|
838
|
+
const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
|
|
836
839
|
fromRole, toRole, fromEntitySet, toEntitySet);
|
|
837
840
|
if(navigationProperty._csn._SetAttributes)
|
|
838
841
|
assocSet.setSapVocabularyAsAttributes(navigationProperty._csn._SetAttributes);
|
|
@@ -846,6 +849,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
846
849
|
return schemaAliasPrefix + name.replace(schemaNamePrefix, '')
|
|
847
850
|
}
|
|
848
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
|
+
}
|
|
849
878
|
}
|
|
850
879
|
}
|
|
851
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)
|