@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/edm/csn2edm.js
CHANGED
|
@@ -7,7 +7,7 @@ const NAVPROP_TRENNER = '_';
|
|
|
7
7
|
const VALUELIST_NAVPROP_PREFIX = '';
|
|
8
8
|
|
|
9
9
|
const edmUtils = require('./edmUtils.js')
|
|
10
|
-
const { initializeModel
|
|
10
|
+
const { initializeModel } = require('./edmPreprocessor.js');
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
12
|
const { setProp } = require('../base/model');
|
|
13
13
|
const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
|
|
@@ -326,15 +326,16 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
326
326
|
|
|
327
327
|
/* create the entitytypes and sets
|
|
328
328
|
Do not create an entity set if:
|
|
329
|
-
V4 containment:
|
|
329
|
+
V4 containment: $containerNames is set and not equal with the artifact name
|
|
330
330
|
Entity starts with 'localserviceNameized.' or ends with '_localized'
|
|
331
331
|
*/
|
|
332
332
|
edmUtils.foreach(schemaCsn.definitions,
|
|
333
|
-
a =>
|
|
333
|
+
a => a.kind === 'entity' && !a.abstract && a.name.startsWith(schemaNamePrefix),
|
|
334
334
|
createEntityTypeAndSet
|
|
335
335
|
);
|
|
336
336
|
// create unbound actions/functions
|
|
337
|
-
edmUtils.foreach(schemaCsn.definitions,
|
|
337
|
+
edmUtils.foreach(schemaCsn.definitions,
|
|
338
|
+
a => (a.kind === 'action' || a.kind === 'function') && a.name.startsWith(schemaNamePrefix),
|
|
338
339
|
(options.isV4()) ? createActionV4 : createActionV2);
|
|
339
340
|
|
|
340
341
|
// create the complex types
|
|
@@ -346,7 +347,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
346
347
|
{
|
|
347
348
|
edmUtils.foreach(schemaCsn.definitions,
|
|
348
349
|
artifact => edmUtils.isDerivedType(artifact) &&
|
|
349
|
-
!
|
|
350
|
+
!artifact.target &&
|
|
350
351
|
artifact.name.startsWith(schemaNamePrefix),
|
|
351
352
|
createTypeDefinition);
|
|
352
353
|
}
|
|
@@ -395,14 +396,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
395
396
|
{
|
|
396
397
|
const EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
|
|
397
398
|
const EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
|
|
398
|
-
|
|
399
|
+
const isSingleton = edmUtils.isSingleton(entityCsn) && options.isV4();
|
|
399
400
|
const [ properties, hasStream ] = createProperties(entityCsn);
|
|
400
401
|
|
|
401
402
|
const loc = ['definitions', entityCsn.name];
|
|
402
403
|
const type = `${schema.name}.${EntityTypeName}`;
|
|
403
404
|
if(properties.length === 0)
|
|
404
405
|
warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no properties');
|
|
405
|
-
else if(entityCsn.$edmKeyPaths.length === 0)
|
|
406
|
+
else if(entityCsn.$edmKeyPaths.length === 0 && !isSingleton)
|
|
406
407
|
message('odata-spec-violation-no-key', loc);
|
|
407
408
|
|
|
408
409
|
if(!edmUtils.isODataSimpleIdentifier(EntityTypeName))
|
|
@@ -414,7 +415,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
414
415
|
if(p._edmAttributes.Name === EntityTypeName)
|
|
415
416
|
warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
|
|
416
417
|
|
|
417
|
-
if(options.isV2() && p._isCollection && !
|
|
418
|
+
if(options.isV2() && p._isCollection && !p._csn.target)
|
|
418
419
|
warning('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
419
420
|
|
|
420
421
|
if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
|
|
@@ -432,7 +433,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
432
433
|
// CDXCORE-CDXCORE-173
|
|
433
434
|
if(options.isV2() && hasStream) {
|
|
434
435
|
attributes['m:HasStream'] = true;
|
|
435
|
-
assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
|
|
436
|
+
edmUtils.assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
|
|
436
437
|
}
|
|
437
438
|
|
|
438
439
|
Schema.append(new Edm.EntityType(v, attributes, properties, entityCsn));
|
|
@@ -461,7 +462,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
// put actions behind entity types in Schema/EntityContainer
|
|
464
|
-
|
|
465
|
+
entityCsn.actions && Object.entries(entityCsn.actions).forEach(([ n, a ]) => {
|
|
465
466
|
(options.isV4()) ? createActionV4(a, n, entityCsn)
|
|
466
467
|
: createActionV2(a, n, entityCsn)
|
|
467
468
|
});
|
|
@@ -522,7 +523,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
522
523
|
}
|
|
523
524
|
|
|
524
525
|
// Parameter Nodes
|
|
525
|
-
|
|
526
|
+
actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
|
|
526
527
|
const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
|
|
527
528
|
const pLoc = [ ...loc, 'params', p._edmAttributes.Name ];
|
|
528
529
|
if(!edmUtils.isODataSimpleIdentifier(parameterName))
|
|
@@ -573,7 +574,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
573
574
|
if(rt) // add EntitySet attribute only if return type is an entity
|
|
574
575
|
{
|
|
575
576
|
const defintion = schemaCsn.definitions[rt];
|
|
576
|
-
if(defintion &&
|
|
577
|
+
if(defintion && defintion.kind === 'entity')
|
|
577
578
|
{
|
|
578
579
|
functionImport.setEdmAttribute('EntitySet', rt.replace(schemaNamePrefix, ''));
|
|
579
580
|
}
|
|
@@ -597,7 +598,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
597
598
|
// Binding Parameter: Primary Keys at first position in sequence, this is decisive!
|
|
598
599
|
// V2 XML: Nullable=false is set because we reuse the primary key property for the parameter
|
|
599
600
|
edmUtils.foreach(entityCsn.elements,
|
|
600
|
-
elementCsn => elementCsn.key && !
|
|
601
|
+
elementCsn => elementCsn.key && !elementCsn.target,
|
|
601
602
|
(elementCsn, elementName) => {
|
|
602
603
|
functionImport.append(new Edm.Parameter(v, { Name: elementName }, elementCsn, 'In' ));
|
|
603
604
|
}
|
|
@@ -605,7 +606,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
605
606
|
}
|
|
606
607
|
|
|
607
608
|
// is this still required?
|
|
608
|
-
|
|
609
|
+
Object.entries(actionCsn).forEach(([p, v]) => {
|
|
609
610
|
if (p.match(/^@sap\./))
|
|
610
611
|
functionImport.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v });
|
|
611
612
|
});
|
|
@@ -613,7 +614,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
613
614
|
// V2 XML: Parameters that are not explicitly marked as Nullable or NotNullable in the CSN must become Nullable=true
|
|
614
615
|
// V2 XML spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
|
|
615
616
|
// the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
|
|
616
|
-
|
|
617
|
+
actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
|
|
617
618
|
const pLoc = [...loc, 'params', parameterName];
|
|
618
619
|
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
619
620
|
edmTypeCompatibilityCheck(param, pLoc);
|
|
@@ -683,14 +684,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
683
684
|
let hasStream = false;
|
|
684
685
|
const streamProps = [];
|
|
685
686
|
|
|
686
|
-
|
|
687
|
+
elementsCsn.elements && Object.entries(elementsCsn.elements).forEach(([elementName, elementCsn]) =>
|
|
687
688
|
{
|
|
688
689
|
if(elementCsn._edmParentCsn == undefined)
|
|
689
690
|
setProp(elementCsn, '_edmParentCsn', edmParentCsn);
|
|
690
691
|
|
|
691
|
-
if(
|
|
692
|
-
if(edmUtils.isAssociationOrComposition(elementCsn))
|
|
693
|
-
{
|
|
692
|
+
if(elementCsn.target) {
|
|
694
693
|
// Foreign keys are part of the generic elementCsn.elements property creation
|
|
695
694
|
|
|
696
695
|
// This is the V4 edmx:NavigationProperty
|
|
@@ -698,36 +697,35 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
698
697
|
|
|
699
698
|
// suppress navprop creation only if @odata.navigable:false is not annotated.
|
|
700
699
|
// (undefined !== false) still evaluates to true
|
|
701
|
-
|
|
700
|
+
if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
|
|
702
701
|
{
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
702
|
+
const navProp = new Edm.NavigationProperty(v, {
|
|
703
|
+
Name: elementName,
|
|
704
|
+
Type: elementCsn._target.name
|
|
705
|
+
}, elementCsn);
|
|
706
|
+
props.push(navProp);
|
|
708
707
|
// save the navProp in the global array for late constraint building
|
|
709
|
-
|
|
710
|
-
}
|
|
708
|
+
navigationProperties.push(navProp);
|
|
711
709
|
}
|
|
710
|
+
}
|
|
712
711
|
// render ordinary property if element is NOT ...
|
|
713
712
|
// 1) ... annotated @cds.api.ignore
|
|
714
713
|
// 2) ... annotated @odata.foreignKey4 and odataFormat: structured
|
|
715
714
|
|
|
716
|
-
|
|
715
|
+
else if(isEdmPropertyRendered(elementCsn, options))
|
|
717
716
|
{
|
|
718
717
|
// CDXCORE-CDXCORE-173
|
|
719
718
|
// V2: filter @Core.MediaType
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
719
|
+
if ( options.isV2() && elementCsn['@Core.MediaType']) {
|
|
720
|
+
hasStream = elementCsn['@Core.MediaType'];
|
|
721
|
+
delete elementCsn['@Core.MediaType'];
|
|
723
722
|
// CDXCORE-CDXCORE-177:
|
|
724
723
|
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
725
724
|
// V4: render property type 'Edm.Stream'
|
|
726
|
-
|
|
725
|
+
streamProps.push(elementName);
|
|
727
726
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
}
|
|
727
|
+
} else {
|
|
728
|
+
props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
|
|
731
729
|
}
|
|
732
730
|
}
|
|
733
731
|
|
|
@@ -772,10 +770,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
772
770
|
message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
|
|
773
771
|
|
|
774
772
|
if(options.isV2()) {
|
|
775
|
-
if(p._isCollection && !
|
|
773
|
+
if(p._isCollection && !p._csn.target)
|
|
776
774
|
warning('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
777
775
|
|
|
778
|
-
if(
|
|
776
|
+
if(p._csn.target)
|
|
779
777
|
warning('odata-spec-violation-assoc', pLoc, { version: '2.0' });
|
|
780
778
|
}
|
|
781
779
|
});
|
package/lib/edm/edm.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const edmUtils = require('./edmUtils.js');
|
|
4
4
|
const { isBuiltinType } = require('../model/csnUtils.js');
|
|
5
5
|
const { forEach } = require("../utils/objectUtils");
|
|
6
|
+
const { isBetaEnabled } = require('../base/model.js');
|
|
6
7
|
|
|
7
8
|
// facet definitions, optional could either be true or array of edm types
|
|
8
9
|
// remove indicates wether or not the canonic facet shall be removed when applying @odata.Type
|
|
@@ -239,7 +240,7 @@ function getEdm(options, messageFunctions) {
|
|
|
239
240
|
if(csn)
|
|
240
241
|
{
|
|
241
242
|
const attr = (useSetAttributes ? csn._SetAttributes : csn);
|
|
242
|
-
|
|
243
|
+
attr && Object.entries(attr).forEach(([p, v]) => {
|
|
243
244
|
if (p.match(/^@sap./))
|
|
244
245
|
this.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v } );
|
|
245
246
|
});
|
|
@@ -332,7 +333,7 @@ function getEdm(options, messageFunctions) {
|
|
|
332
333
|
if(what==='metadata' || what==='all')
|
|
333
334
|
{
|
|
334
335
|
xml += super.innerXML(indent);
|
|
335
|
-
|
|
336
|
+
this._actions && Object.values(this._actions).forEach(actionArray => {
|
|
336
337
|
actionArray.forEach(action => {
|
|
337
338
|
xml += action.toXML(indent, what) + '\n'; });
|
|
338
339
|
});
|
|
@@ -350,7 +351,7 @@ function getEdm(options, messageFunctions) {
|
|
|
350
351
|
// no $Namespace
|
|
351
352
|
toJSONattributes(json)
|
|
352
353
|
{
|
|
353
|
-
|
|
354
|
+
this._edmAttributes && Object.entries(this._edmAttributes).forEach(([p, v]) => {
|
|
354
355
|
if (p !== 'Name' && p !== 'Namespace')
|
|
355
356
|
json[p[0] === '@' ? p : '$' + p] = v;
|
|
356
357
|
});
|
|
@@ -371,7 +372,7 @@ function getEdm(options, messageFunctions) {
|
|
|
371
372
|
if(Object.keys(json_Annotations).length)
|
|
372
373
|
json['$Annotations'] = json_Annotations;
|
|
373
374
|
}
|
|
374
|
-
|
|
375
|
+
this._actions && Object.entries(this._actions).forEach(([actionName, actionArray]) => {
|
|
375
376
|
json[actionName] = [];
|
|
376
377
|
actionArray.forEach(action => {
|
|
377
378
|
json[actionName].push(action.toJSON());
|
|
@@ -737,7 +738,7 @@ function getEdm(options, messageFunctions) {
|
|
|
737
738
|
if(this._type !== 'Edm.String' && this._type) // Edm.String is default)
|
|
738
739
|
json['$'+this._typeName] = this._type;
|
|
739
740
|
|
|
740
|
-
|
|
741
|
+
this._edmAttributes && Object.entries(this._edmAttributes).forEach(([p, v]) => {
|
|
741
742
|
if (p !== 'Name' && p !== this._typeName
|
|
742
743
|
// remove this line if Nullable=true becomes default
|
|
743
744
|
&& !(p === 'Nullable' && v == false))
|
|
@@ -754,7 +755,14 @@ function getEdm(options, messageFunctions) {
|
|
|
754
755
|
}
|
|
755
756
|
}
|
|
756
757
|
|
|
757
|
-
class ComplexType extends TypeBase {
|
|
758
|
+
class ComplexType extends TypeBase {
|
|
759
|
+
constructor(v, details, csn) {
|
|
760
|
+
super(v, details, csn);
|
|
761
|
+
if(this.v4 && !!csn['@open'] && isBetaEnabled(options, 'odataOpenType')) {
|
|
762
|
+
this._edmAttributes['OpenType'] = true;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
758
766
|
class EntityType extends ComplexType
|
|
759
767
|
{
|
|
760
768
|
constructor(v, details, properties, csn)
|
|
@@ -840,8 +848,8 @@ function getEdm(options, messageFunctions) {
|
|
|
840
848
|
super(v, attributes, csn);
|
|
841
849
|
|
|
842
850
|
// array of enum not yet allowed
|
|
843
|
-
|
|
844
|
-
|
|
851
|
+
const enumValues = /*(csn.items && csn.items.enum) ||*/ csn.enum;
|
|
852
|
+
enumValues && Object.entries(enumValues).forEach(([en, e]) => {
|
|
845
853
|
this.append(new Member(v, { Name: en, Value: e.val } ));
|
|
846
854
|
});
|
|
847
855
|
}
|
|
@@ -1177,9 +1185,10 @@ function getEdm(options, messageFunctions) {
|
|
|
1177
1185
|
_constraints = this._csn._constraints._partnerCsn._constraints;
|
|
1178
1186
|
[i,j] = [1,0];
|
|
1179
1187
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1188
|
+
_constraints.constraints && Object.values(_constraints.constraints).forEach(c =>
|
|
1189
|
+
this.append(new ReferentialConstraint(this._v,
|
|
1190
|
+
{ Property: c[i].join(options.pathDelimiter), ReferencedProperty: c[j].join(options.pathDelimiter) } ) )
|
|
1191
|
+
);
|
|
1183
1192
|
}
|
|
1184
1193
|
}
|
|
1185
1194
|
|
|
@@ -1502,7 +1511,7 @@ function getEdm(options, messageFunctions) {
|
|
|
1502
1511
|
}
|
|
1503
1512
|
toJSONattributes(json) {
|
|
1504
1513
|
super.toJSONattributes(json);
|
|
1505
|
-
|
|
1514
|
+
this._jsonOnlyAttributes && Object.entries(this._jsonOnlyAttributes).forEach(([p, v]) => {
|
|
1506
1515
|
json[p[0] === '@' ? p : '$' + p] = v;
|
|
1507
1516
|
});
|
|
1508
1517
|
return json;
|
|
@@ -1606,7 +1615,7 @@ function getEdm(options, messageFunctions) {
|
|
|
1606
1615
|
node._d = new Dependent(v, { Role: from } );
|
|
1607
1616
|
node._p = new Principal(v, { Role: to } );
|
|
1608
1617
|
|
|
1609
|
-
|
|
1618
|
+
c && Object.values(c).forEach(cv => {
|
|
1610
1619
|
node._d.append(new PropertyRef(v, cv[0].join(options.pathDelimiter)));
|
|
1611
1620
|
node._p.append(new PropertyRef(v, cv[1].join(options.pathDelimiter)));
|
|
1612
1621
|
});
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const edmUtils = require('./edmUtils.js');
|
|
4
|
+
const { setProp } = require('../base/model');
|
|
5
|
+
const { forEachGeneric } = require('../model/csnUtils');
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Late application specific transformations
|
|
9
|
+
* At present there are two transformation targets: Structure and Element
|
|
10
|
+
* These transformations are available today:
|
|
11
|
+
*
|
|
12
|
+
* Analytical Scenario:
|
|
13
|
+
* If a structure is annotated with @Aggregation.ApplySupported.PropertyRestrictions
|
|
14
|
+
* then a number of annotation rewrites are done to this structure and to the
|
|
15
|
+
* elements of this structure
|
|
16
|
+
* Also the key properties of all structure elements are removed and a new
|
|
17
|
+
* artificial key element 'key _ID : String' is inserted at first position of
|
|
18
|
+
* the elements dictionary
|
|
19
|
+
*
|
|
20
|
+
* PDM (Personal Data Management)
|
|
21
|
+
* Planned but not yet implemented annotation rewriting (pending to finalization)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* eslint max-statements-per-line:off */
|
|
25
|
+
|
|
26
|
+
function mapAnnotationAssignment(artifact, parent, mappingDictionary)
|
|
27
|
+
{
|
|
28
|
+
let props = edmUtils.intersect(Object.keys(mappingDictionary), Object.keys(artifact));
|
|
29
|
+
// now start the substitution
|
|
30
|
+
props.forEach(prop => {
|
|
31
|
+
let [ mapping, value, remove_original ] = mappingDictionary[prop];
|
|
32
|
+
if(mapping instanceof Function)
|
|
33
|
+
{
|
|
34
|
+
mapping(artifact, parent, prop);
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
{
|
|
38
|
+
edmUtils.assignAnnotation(artifact, mapping, value || artifact[prop]['='] || artifact[prop]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if(remove_original)
|
|
42
|
+
delete artifact[prop];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function addToSetAttr(carrier, propName, propValue, removeFromType=true) {
|
|
47
|
+
edmUtils.assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
48
|
+
edmUtils.assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
49
|
+
if(removeFromType) {
|
|
50
|
+
delete carrier[propName];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function applyAppSpecificLateCsnTransformationOnElement(options, element, struct, error)
|
|
55
|
+
{
|
|
56
|
+
if(options.isV2())
|
|
57
|
+
{
|
|
58
|
+
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
59
|
+
{
|
|
60
|
+
mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
|
|
61
|
+
}
|
|
62
|
+
mapAnnotationAssignment(element, struct, PDMSemantics());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
|
|
66
|
+
// Oliver Heinrich mentions in the issue that the Okra runtime must be set to a
|
|
67
|
+
// concurrent runtime mode by the caller, if the annotation is added this late,
|
|
68
|
+
// it doesn't appear in the forOData processed CSN, meaning that the
|
|
69
|
+
// runtime cannot set that okra flag (alternatively the runtime has to search
|
|
70
|
+
// for @[odata|cds].etag annotations...
|
|
71
|
+
if(options.isV4())
|
|
72
|
+
{
|
|
73
|
+
if(element['@odata.etag'] == true || element['@cds.etag'] == true) {
|
|
74
|
+
// don't put element name into collection as per advice from Ralf Handl, as
|
|
75
|
+
// no runtime is interested in the property itself, it is sufficient to mark
|
|
76
|
+
// the entity set.
|
|
77
|
+
edmUtils.assignAnnotation(struct, '@Core.OptimisticConcurrency',
|
|
78
|
+
(struct['@Core.OptimisticConcurrency'] || [])/*.push(element.name)*/);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// nested functions begin
|
|
83
|
+
function PDMSemantics()
|
|
84
|
+
{
|
|
85
|
+
/*
|
|
86
|
+
let dict = Object.create(null);
|
|
87
|
+
|
|
88
|
+
dict['@PDM.xxx1'] = [ '@sap.pdm-semantics' ];
|
|
89
|
+
dict['@PDM.xxx2'] = [ '@sap.pdm-propery' ];
|
|
90
|
+
dict['@PDM.xxx3'] = [ '@sap.pdm-display-sq-no' ];
|
|
91
|
+
dict['@PDM.xxx4'] = [ '@sap.pdm-record-identifier' ];
|
|
92
|
+
dict['@PDM.xxx5'] = [ '@sap.pdm-field-group' ];
|
|
93
|
+
dict['@PDM.xxx6'] = [ '@sap.pdm-mask-find-pattern' ];
|
|
94
|
+
dict['@PDM.xxx7'] = [ '@sap.pdm-mask-replacement-pattern' ];
|
|
95
|
+
dict['@PDM.xxx8'] = [ '@sap.deletable' ];
|
|
96
|
+
dict['@PDM.xxx8'] = [ '@sap.updatable' ];
|
|
97
|
+
|
|
98
|
+
// respect flattened annotation $value
|
|
99
|
+
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
100
|
+
*/
|
|
101
|
+
return Object.create(null);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function AnalyticalAnnotations()
|
|
105
|
+
{
|
|
106
|
+
function mapCommonAttributes(element, struct, prop)
|
|
107
|
+
{
|
|
108
|
+
let CommonAttributes = element[prop];
|
|
109
|
+
if(!Array.isArray(CommonAttributes)) {
|
|
110
|
+
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
111
|
+
{ anno: '@Common.attribute', code: JSON.stringify(CommonAttributes) },
|
|
112
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let targets = edmUtils.intersect(CommonAttributes, Object.keys(struct.elements));
|
|
117
|
+
targets.forEach(tgt => {
|
|
118
|
+
edmUtils.assignAnnotation(struct.elements[tgt], '@sap.attribute-for', element.name);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mapContextDefiningProperties(element, struct, prop)
|
|
123
|
+
{
|
|
124
|
+
let ContextDefiningProperties = element[prop];
|
|
125
|
+
if(!Array.isArray(ContextDefiningProperties)) {
|
|
126
|
+
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
127
|
+
{ anno: '@Aggregation.ContextDefiningProperties', code: JSON.stringify(ContextDefiningProperties) },
|
|
128
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if(ContextDefiningProperties.length > 0)
|
|
132
|
+
edmUtils.assignAnnotation(element, '@sap.super-ordinate', ContextDefiningProperties[ContextDefiningProperties.length-1]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let dict = Object.create(null);
|
|
136
|
+
//analytics term definition unknown, lower case
|
|
137
|
+
dict['@Analytics.Measure'] = [ '@sap.aggregation-role', 'measure' ];
|
|
138
|
+
dict['@Analytics.Dimension'] = [ '@sap.aggregation-role', 'dimension' ];
|
|
139
|
+
dict['@Semantics.currencyCode'] = [ '@sap.semantics', 'currency-code', true ];
|
|
140
|
+
dict['@Semantics.unitOfMeasure'] = [ '@sap.semantics', 'unit-of-measure', true ];
|
|
141
|
+
|
|
142
|
+
dict['@Measures.ISOCurrency'] = [ '@sap.unit' ];
|
|
143
|
+
dict['@Measures.Unit'] = [ '@sap.unit' ];
|
|
144
|
+
|
|
145
|
+
dict['@Common.Label'] = [ '@sap.label' ];
|
|
146
|
+
dict['@Common.Text'] = [ '@sap.text' ];
|
|
147
|
+
dict['@Aggregation.ContextDefiningProperties'] = [ mapContextDefiningProperties ];
|
|
148
|
+
dict['@Common.Attributes'] = [ mapCommonAttributes ];
|
|
149
|
+
|
|
150
|
+
// respect flattened annotation $value
|
|
151
|
+
Object.entries(dict).forEach(([k, v]) => dict[k+'.$value'] = v);
|
|
152
|
+
return dict;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error)
|
|
157
|
+
{
|
|
158
|
+
if(options.isV2())
|
|
159
|
+
{
|
|
160
|
+
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
161
|
+
{
|
|
162
|
+
transformAnalyticalModel(struct);
|
|
163
|
+
mapAnnotationAssignment(struct, undefined, AnalyticalAnnotations());
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// nested functions begin
|
|
168
|
+
function transformAnalyticalModel(struct)
|
|
169
|
+
{
|
|
170
|
+
let keyName = 'ID__';
|
|
171
|
+
if(struct == undefined || struct.elements == undefined || struct.elements[keyName] != undefined)
|
|
172
|
+
return;
|
|
173
|
+
|
|
174
|
+
// remove key prop from elements, add new key to elements
|
|
175
|
+
let elements = Object.create(null);
|
|
176
|
+
let key = { name: keyName, key : true, type : 'cds.String', '@sap.sortable':false, '@sap.filterable':false, '@UI.Hidden': true };
|
|
177
|
+
elements[keyName] = key;
|
|
178
|
+
setProp(struct, '$keys',{ [keyName] : key } );
|
|
179
|
+
forEachGeneric(struct.items || struct, 'elements', (e,n) =>
|
|
180
|
+
{
|
|
181
|
+
if(e.key) delete e.key;
|
|
182
|
+
elements[n] = e;
|
|
183
|
+
});
|
|
184
|
+
struct.elements = elements;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function AnalyticalAnnotations()
|
|
188
|
+
{
|
|
189
|
+
function mapFilterRestrictions(struct, parent, prop)
|
|
190
|
+
{
|
|
191
|
+
let stringDict = Object.create(null);
|
|
192
|
+
stringDict['SingleValue'] = 'single-value';
|
|
193
|
+
stringDict['MultiValue'] = 'multi-value';
|
|
194
|
+
stringDict['SingleRange'] = 'interval';
|
|
195
|
+
|
|
196
|
+
let filterRestrictions = struct[prop];
|
|
197
|
+
if(!Array.isArray(filterRestrictions)) {
|
|
198
|
+
error(null, ['definitions', struct.name ],
|
|
199
|
+
{ anno: '@Capabilities.FilterRestrictions.FilterExpressionRestrictions',
|
|
200
|
+
code: JSON.stringify(filterRestrictions) },
|
|
201
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
filterRestrictions.forEach(v => {
|
|
205
|
+
let e = struct.elements[v.Property];
|
|
206
|
+
if(e)
|
|
207
|
+
edmUtils.assignAnnotation(e, '@sap.filter-restriction', stringDict[v.AllowedExpressions]);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function mapRequiredProperties(struct, parent, prop)
|
|
212
|
+
{
|
|
213
|
+
let requiredProperties = struct[prop];
|
|
214
|
+
if(!Array.isArray(requiredProperties)) {
|
|
215
|
+
error(null, ['definitions', struct.name],
|
|
216
|
+
{ anno: '@Capabilities.FilterRestrictions.RequiredProperties',
|
|
217
|
+
code: JSON.stringify(requiredProperties) },
|
|
218
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let props = edmUtils.intersect(Object.keys(struct.elements), requiredProperties)
|
|
223
|
+
props.forEach(p => {
|
|
224
|
+
edmUtils.assignAnnotation(struct.elements[p], '@sap.required-in-filter', true);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function mapRequiresFilter(struct, parent, prop)
|
|
229
|
+
{
|
|
230
|
+
let requiresFilter = struct[prop];
|
|
231
|
+
if(requiresFilter)
|
|
232
|
+
edmUtils.assignAnnotation(struct._SetAttributes, '@sap.requires-filter', requiresFilter);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Entity Props
|
|
236
|
+
let dict = Object.create(null);
|
|
237
|
+
dict['@Aggregation.ApplySupported.PropertyRestrictions'] = [ '@sap.semantics', 'aggregate' ];
|
|
238
|
+
dict['@Common.Label'] = [ '@sap.label' ];
|
|
239
|
+
dict['@Capabilities.FilterRestrictions.RequiresFilter'] = [ mapRequiresFilter ];
|
|
240
|
+
dict['@Capabilities.FilterRestrictions.RequiredProperties'] = [ mapRequiredProperties ];
|
|
241
|
+
dict['@Capabilities.FilterRestrictions.FilterExpressionRestrictions'] = [ mapFilterRestrictions ];
|
|
242
|
+
|
|
243
|
+
// respect flattened annotation $value
|
|
244
|
+
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
245
|
+
|
|
246
|
+
return dict;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function setSAPSpecificV2AnnotationsToEntityContainer(options, carrier) {
|
|
251
|
+
if(!options.isV2())
|
|
252
|
+
return;
|
|
253
|
+
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntityContainer
|
|
254
|
+
const SetAttributes = {
|
|
255
|
+
// EntityContainer only
|
|
256
|
+
'@sap.supported.formats' : addToSetAttr,
|
|
257
|
+
'@sap.use.batch': addToSetAttr,
|
|
258
|
+
'@sap.message.scope.supported': addToSetAttr,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
Object.entries(carrier).forEach(([p, v]) => {
|
|
262
|
+
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
267
|
+
if(!options.isV2())
|
|
268
|
+
return;
|
|
269
|
+
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntitySet
|
|
270
|
+
const SetAttributes = {
|
|
271
|
+
// EntitySet, EntityType
|
|
272
|
+
'@sap.label' : (s,pn, pv) => { addToSetAttr(s, pn, pv, false); },
|
|
273
|
+
'@sap.semantics': checkSemantics,
|
|
274
|
+
// EntitySet only
|
|
275
|
+
'@sap.creatable' : addToSetAttr,
|
|
276
|
+
'@sap.updatable' : addToSetAttr,
|
|
277
|
+
'@sap.deletable': addToSetAttr,
|
|
278
|
+
'@sap.updatable.path': addToSetAttr,
|
|
279
|
+
'@sap.deletable.path': addToSetAttr,
|
|
280
|
+
'@sap.searchable' : addToSetAttr,
|
|
281
|
+
'@sap.pagable': addToSetAttr,
|
|
282
|
+
'@sap.topable': addToSetAttr,
|
|
283
|
+
'@sap.countable': addToSetAttr,
|
|
284
|
+
'@sap.addressable': addToSetAttr,
|
|
285
|
+
'@sap.requires.filter': addToSetAttr,
|
|
286
|
+
'@sap.change.tracking': addToSetAttr,
|
|
287
|
+
'@sap.maxpagesize': addToSetAttr,
|
|
288
|
+
'@sap.delta.link.validity': addToSetAttr,
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
Object.entries(carrier).forEach(([p, v]) => {
|
|
292
|
+
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
function checkSemantics(struct, propName, propValue) {
|
|
296
|
+
if(propValue === 'timeseries' || propValue === 'aggregate') {
|
|
297
|
+
// aggregate is forwarded to Set and must remain on Type
|
|
298
|
+
addToSetAttr(struct, propName, propValue, propValue !== 'aggregate');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
304
|
+
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0
|
|
305
|
+
const SetAttributes = {
|
|
306
|
+
// Applicable to NavProp and foreign keys, add to AssociationSet
|
|
307
|
+
'@sap.creatable' : (c, pn, pv) => { addToAssociationSet(c, pn, pv, false); },
|
|
308
|
+
// Not applicable to NavProp, applicable to foreign keys, add to AssociationSet
|
|
309
|
+
'@sap.updatable' : addToAssociationSet,
|
|
310
|
+
// Not applicable to NavProp, not applicable to foreign key, add to AssociationSet
|
|
311
|
+
'@sap.deletable': (c, pn, pv) => {
|
|
312
|
+
addToAssociationSet(c, pn, pv);
|
|
313
|
+
removeFromForeignKey(c, pn);
|
|
314
|
+
},
|
|
315
|
+
// applicable to NavProp, not applicable to foreign keys, not applicable to AssociationSet
|
|
316
|
+
'@sap.creatable.path': removeFromForeignKey,
|
|
317
|
+
'@sap.filterable': removeFromForeignKey,
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
Object.entries(carrier).forEach(([p, v]) => {
|
|
321
|
+
(SetAttributes[p] || function() {/* no-op */})(carrier, p, v);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
function addToAssociationSet(carrier, propName, propValue, removeFromType=true) {
|
|
325
|
+
if(carrier.target) {
|
|
326
|
+
edmUtils.assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
327
|
+
edmUtils.assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
328
|
+
if(removeFromType) {
|
|
329
|
+
delete carrier[propName];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function removeFromForeignKey(carrier, propName) {
|
|
335
|
+
if(carrier['@odata.foreignKey4'] && carrier[propName] !== undefined) {
|
|
336
|
+
delete carrier[propName];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
module.exports = {
|
|
344
|
+
applyAppSpecificLateCsnTransformationOnElement,
|
|
345
|
+
applyAppSpecificLateCsnTransformationOnStructure,
|
|
346
|
+
setSAPSpecificV2AnnotationsToEntityContainer,
|
|
347
|
+
setSAPSpecificV2AnnotationsToEntitySet,
|
|
348
|
+
setSAPSpecificV2AnnotationsToAssociation
|
|
349
|
+
};
|