@sap/cds-compiler 2.11.4 → 2.12.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 +58 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +9 -10
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +12 -0
- package/lib/api/main.js +2 -0
- package/lib/api/options.js +2 -2
- package/lib/base/message-registry.js +31 -2
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +97 -69
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/checks.js +32 -9
- package/lib/compiler/definer.js +25 -4
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolver.js +97 -6
- package/lib/compiler/shared.js +12 -1
- package/lib/compiler/utils.js +7 -0
- package/lib/edm/annotations/genericTranslation.js +34 -17
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +1 -1
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +30 -23
- package/lib/edm/edmUtils.js +11 -12
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +15 -14
- package/lib/gen/languageLexer.interp +9 -1
- package/lib/gen/languageLexer.js +830 -779
- package/lib/gen/languageLexer.tokens +7 -6
- package/lib/gen/languageParser.js +2401 -2282
- package/lib/json/from-csn.js +47 -16
- package/lib/json/to-csn.js +17 -5
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/genericAntlrParser.js +68 -51
- package/lib/language/language.g4 +128 -74
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +5 -3
- package/lib/main.js +3 -2
- package/lib/model/csnRefs.js +116 -68
- package/lib/model/csnUtils.js +40 -48
- package/lib/model/enrichCsn.js +30 -14
- package/lib/optionProcessor.js +3 -3
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +193 -79
- package/lib/render/toHdbcds.js +179 -95
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +57 -40
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +6 -4
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +4 -5
- package/lib/transform/db/flattening.js +5 -6
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +36 -23
- package/lib/transform/forHanaNew.js +35 -626
- package/lib/transform/forOdataNew.js +5 -4
- package/lib/transform/localized.js +3 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +13 -13
- package/lib/transform/translateAssocsToJoins.js +8 -8
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +2 -1
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
|
@@ -207,7 +207,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
207
207
|
// Note: we assume that all objects ly flat in the service, i.e. objName always
|
|
208
208
|
// looks like <service name, can contain dots>.<id>
|
|
209
209
|
forEachDefinition(csn, (object, objName) => {
|
|
210
|
-
if(objName
|
|
210
|
+
if (objName === serviceName || objName.startsWith(serviceName + '.')) {
|
|
211
211
|
if (object.kind === 'action' || object.kind === 'function') {
|
|
212
212
|
handleAction(objName, object, null);
|
|
213
213
|
}
|
|
@@ -505,8 +505,17 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
505
505
|
let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
|
|
506
506
|
|
|
507
507
|
if (carrier.kind === 'entity' || carrier.kind === 'view') {
|
|
508
|
-
// If AppliesTo=[EntitySet/Singleton, EntityType], EntitySet/Singleton has precedence
|
|
509
|
-
testToAlternativeEdmTarget = (x =>
|
|
508
|
+
// If AppliesTo=[EntitySet/Singleton/Collection, EntityType], EntitySet/Singleton/Collection has precedence
|
|
509
|
+
testToAlternativeEdmTarget = (x => {
|
|
510
|
+
if(options.isV2()) {
|
|
511
|
+
return ['Singleton', 'EntitySet', 'Collection'].some(y => x.includes(y));
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
return edmUtils.isSingleton(carrier)
|
|
515
|
+
? x.includes('Singleton')
|
|
516
|
+
: ['EntitySet', 'Collection'].some(y => x.includes(y));
|
|
517
|
+
}
|
|
518
|
+
});
|
|
510
519
|
testToStandardEdmTarget = (x => x ? x.includes('EntityType') : true);
|
|
511
520
|
// if carrier has an alternate 'entitySetName' use this instead of EdmTargetName
|
|
512
521
|
// (see edmPreprocessor.initializeParameterizedEntityOrView(), where parameterized artifacts
|
|
@@ -534,15 +543,21 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
534
543
|
alternativeEdmTargetName = edmTargetName;
|
|
535
544
|
hasAlternativeCarrier = true; // EntityContainer is always available
|
|
536
545
|
}
|
|
537
|
-
|
|
546
|
+
//element => decide if navprop or normal property
|
|
538
547
|
else if(!carrier.kind) {
|
|
539
|
-
|
|
548
|
+
// if appliesTo is undefined, return true
|
|
540
549
|
if(carrier.target) {
|
|
541
|
-
testToStandardEdmTarget = (x=> x
|
|
550
|
+
testToStandardEdmTarget = (x => x
|
|
551
|
+
? x.includes('NavigationProperty') ||
|
|
552
|
+
carrier.cardinality && carrier.cardinality.max === '*' && x.includes('Collection')
|
|
553
|
+
: true);
|
|
542
554
|
}
|
|
543
555
|
else {
|
|
544
556
|
// this might be more precise if handleAnnotation would know more about the carrier
|
|
545
|
-
testToStandardEdmTarget = (x => x
|
|
557
|
+
testToStandardEdmTarget = (x => x
|
|
558
|
+
? ['Parameter', 'Property'].some(y => x.includes(y) ||
|
|
559
|
+
carrier._isCollection && x.includes('Collection'))
|
|
560
|
+
: true);
|
|
546
561
|
}
|
|
547
562
|
}
|
|
548
563
|
return [
|
|
@@ -851,7 +866,6 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
851
866
|
else if (!expectedType['Members'].includes(enumValue)) {
|
|
852
867
|
message(warning, context, `enumeration type ${ dTypeName } has no value ${ enumValue }`);
|
|
853
868
|
}
|
|
854
|
-
return;
|
|
855
869
|
}
|
|
856
870
|
|
|
857
871
|
// cAnnoValue: array
|
|
@@ -865,8 +879,9 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
865
879
|
}
|
|
866
880
|
|
|
867
881
|
let index = 0;
|
|
868
|
-
for (
|
|
869
|
-
context.stack.push('[' + index
|
|
882
|
+
for (const e of cAnnoValue) {
|
|
883
|
+
context.stack.push('[' + index + ']');
|
|
884
|
+
index++;
|
|
870
885
|
if (e['#']) {
|
|
871
886
|
checkEnumValue(e['#'], dTypeName, context);
|
|
872
887
|
}
|
|
@@ -901,7 +916,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
901
916
|
}
|
|
902
917
|
else {
|
|
903
918
|
// replace all occurrences of '.' by '/' up to first '@'
|
|
904
|
-
val = expr.split('@').map((o,i) => (i
|
|
919
|
+
val = expr.split('@').map((o,i) => (i === 0 ? o.replace(/\./g, '/') : o)).join('@');
|
|
905
920
|
}
|
|
906
921
|
|
|
907
922
|
return {
|
|
@@ -922,9 +937,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
922
937
|
// caller already made sure that val is neither object nor array
|
|
923
938
|
dTypeName = resolveType(dTypeName);
|
|
924
939
|
|
|
925
|
-
if(isEnumType(dTypeName)) {
|
|
940
|
+
if (isEnumType(dTypeName)) {
|
|
926
941
|
const type = getDictType(dTypeName);
|
|
927
|
-
|
|
942
|
+
const expected = type.Members.map(m => `"#${m}"`).join(', ');
|
|
943
|
+
message(warning, context, `found non-enum value "${val}", expected ${expected} for ${dTypeName}`);
|
|
928
944
|
}
|
|
929
945
|
|
|
930
946
|
let typeName = 'String';
|
|
@@ -1154,8 +1170,9 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1154
1170
|
}
|
|
1155
1171
|
|
|
1156
1172
|
let index = 0;
|
|
1157
|
-
for (
|
|
1158
|
-
context.stack.push('[' + index
|
|
1173
|
+
for (const value of annoValue) {
|
|
1174
|
+
context.stack.push('[' + index + ']');
|
|
1175
|
+
index++
|
|
1159
1176
|
|
|
1160
1177
|
// for dealing with the single array entries we unfortunately cannot call handleValue(),
|
|
1161
1178
|
// as the values inside an array are represented differently from the values
|
|
@@ -1214,8 +1231,8 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1214
1231
|
return edmNode;
|
|
1215
1232
|
}
|
|
1216
1233
|
|
|
1217
|
-
if(dynExprs.length === 0) {
|
|
1218
|
-
if (typeof obj === 'object' && obj !== null && !Array.isArray(obj) && Object.keys(obj).length
|
|
1234
|
+
if (dynExprs.length === 0) {
|
|
1235
|
+
if (typeof obj === 'object' && obj !== null && !Array.isArray(obj) && Object.keys(obj).length === 1) {
|
|
1219
1236
|
const k = Object.keys(obj)[0];
|
|
1220
1237
|
const val = obj[k];
|
|
1221
1238
|
edmNode = new Edm.ValueThing(v, k[0] === '$' ? k.slice(1) : k, val );
|
|
@@ -236,7 +236,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
236
236
|
} else {
|
|
237
237
|
let stringFields = Object.keys(vlEntity.elements).filter(
|
|
238
238
|
x => !vlEntity.elements[x].key && vlEntity.elements[x].type === 'cds.String')
|
|
239
|
-
if (stringFields.length
|
|
239
|
+
if (stringFields.length === 1)
|
|
240
240
|
textField = stringFields[0];
|
|
241
241
|
}
|
|
242
242
|
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -420,7 +420,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
420
420
|
/** @type {object} */
|
|
421
421
|
let containerEntry;
|
|
422
422
|
|
|
423
|
-
if(edmUtils.isSingleton(entityCsn
|
|
423
|
+
if(edmUtils.isSingleton(entityCsn) && options.isV4()) {
|
|
424
424
|
containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
|
|
425
425
|
if(entityCsn['@odata.singleton.nullable'])
|
|
426
426
|
containerEntry.Nullable= true;
|
package/lib/edm/edm.js
CHANGED
|
@@ -665,7 +665,7 @@ module.exports = function (options, error) {
|
|
|
665
665
|
json['$'+this._typeName] = this._type;
|
|
666
666
|
|
|
667
667
|
edmUtils.forAll(this, (v,p) => {
|
|
668
|
-
if (p !== 'Name' && p
|
|
668
|
+
if (p !== 'Name' && p !== this._typeName
|
|
669
669
|
// remove this line if Nullable=true becomes default
|
|
670
670
|
&& !(p === 'Nullable' && v == false))
|
|
671
671
|
{
|
|
@@ -711,7 +711,7 @@ module.exports = function (options, error) {
|
|
|
711
711
|
if(alias.length > 28) {
|
|
712
712
|
alias = alias.substr(0, 13)+ '__' +alias.substr(alias.length-13, alias.length);
|
|
713
713
|
}
|
|
714
|
-
alias = alias+'_'+c.toString().padStart(3,0);
|
|
714
|
+
alias = alias+'_'+c.toString().padStart(3, '0');
|
|
715
715
|
}
|
|
716
716
|
else if(alias.length > 32) {
|
|
717
717
|
alias = alias.substr(0, 15)+ '__' +alias.substr(alias.length-15, alias.length);
|
|
@@ -835,7 +835,7 @@ module.exports = function (options, error) {
|
|
|
835
835
|
// Nullable=false is default for EDM JSON representation 4.01
|
|
836
836
|
// When a key explicitly (!) has 'notNull = false', it stays nullable
|
|
837
837
|
return (nodeCsn._NotNullCollection !== undefined ? nodeCsn._NotNullCollection :
|
|
838
|
-
(nodeCsn.key &&
|
|
838
|
+
(nodeCsn.key && nodeCsn.notNull !== false) || nodeCsn.notNull === true);
|
|
839
839
|
}
|
|
840
840
|
|
|
841
841
|
toJSONattributes(json)
|
|
@@ -1159,7 +1159,7 @@ module.exports = function (options, error) {
|
|
|
1159
1159
|
/* short notation for Edm.Boolean, Edm.String and Edm.Float, see internal project:
|
|
1160
1160
|
edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
|
|
1161
1161
|
*/
|
|
1162
|
-
case 'Edm.Boolean':
|
|
1162
|
+
case 'Edm.Boolean':
|
|
1163
1163
|
v = (v=='true'?true:(v=='false'?false:v));
|
|
1164
1164
|
// eslint-no-fallthrough
|
|
1165
1165
|
case 'Edm.String':
|
|
@@ -1275,8 +1275,8 @@ module.exports = function (options, error) {
|
|
|
1275
1275
|
if(this.Type)
|
|
1276
1276
|
json['@type'] = this.Type;
|
|
1277
1277
|
let keys = Object.keys(this).filter(k => k !== 'Type');
|
|
1278
|
-
for(
|
|
1279
|
-
json['$'+
|
|
1278
|
+
for(const key of keys)
|
|
1279
|
+
json['$'+key] = this[key];
|
|
1280
1280
|
}
|
|
1281
1281
|
|
|
1282
1282
|
toJSONchildren(json)
|
|
@@ -1377,7 +1377,7 @@ module.exports = function (options, error) {
|
|
|
1377
1377
|
}
|
|
1378
1378
|
|
|
1379
1379
|
toJSON()
|
|
1380
|
-
{
|
|
1380
|
+
{
|
|
1381
1381
|
// toJSON: depending on number of children unary or n-ary expr
|
|
1382
1382
|
const json = this.mergeJSONAnnotations();
|
|
1383
1383
|
const e = this._children.filter(c=>c.kind !== 'Annotation');
|
|
@@ -1459,7 +1459,7 @@ module.exports = function (options, error) {
|
|
|
1459
1459
|
return json;
|
|
1460
1460
|
}
|
|
1461
1461
|
}
|
|
1462
|
-
// LabeledElementReference is a
|
|
1462
|
+
// LabeledElementReference is a
|
|
1463
1463
|
class LabeledElementReference extends ValueThing {
|
|
1464
1464
|
constructor(v, val) {
|
|
1465
1465
|
super(v, 'LabeledElementReference', val);
|
|
@@ -39,7 +39,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
39
39
|
if (!_options)
|
|
40
40
|
throw Error('Please debug me: initializeModel must be invoked with options');
|
|
41
41
|
|
|
42
|
-
const { info, warning, error, throwWithError } = messageFunctions;
|
|
42
|
+
const { info, warning, error, message, throwWithError } = messageFunctions;
|
|
43
43
|
|
|
44
44
|
const csnUtils = getUtils(csn);
|
|
45
45
|
const {
|
|
@@ -455,8 +455,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
455
455
|
// propagate containment information, if containment is recursive, use parameterCsn.name as _containerEntity
|
|
456
456
|
if(entityCsn._containerEntity) {
|
|
457
457
|
setProp(parameterCsn, '_containerEntity', []);
|
|
458
|
-
for(
|
|
459
|
-
parameterCsn._containerEntity.push((c
|
|
458
|
+
for(const c of entityCsn._containerEntity) {
|
|
459
|
+
parameterCsn._containerEntity.push((c === entityCsn.name) ? parameterCsn.name : c);
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
entityCsn._containerEntity = [ parameterCsn ];
|
|
@@ -489,12 +489,18 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
489
489
|
member.$path[1] = parameterEntityName;
|
|
490
490
|
});
|
|
491
491
|
|
|
492
|
-
|
|
493
|
-
|
|
492
|
+
if(csn.definitions[parameterCsn.name])
|
|
493
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
|
|
494
|
+
else
|
|
495
|
+
csn.definitions[parameterCsn.name] = parameterCsn;
|
|
494
496
|
// modify the original parameter entity with backlink and new name
|
|
495
|
-
csn.definitions[originalEntityName]
|
|
496
|
-
|
|
497
|
-
|
|
497
|
+
if(csn.definitions[originalEntityName])
|
|
498
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: originalEntityName });
|
|
499
|
+
else {
|
|
500
|
+
csn.definitions[originalEntityName] = entityCsn;
|
|
501
|
+
delete csn.definitions[entityCsn.name];
|
|
502
|
+
entityCsn.name = originalEntityName;
|
|
503
|
+
}
|
|
498
504
|
setProp(entityCsn, '$entitySetName', originalEntitySetName);
|
|
499
505
|
// add backlink association
|
|
500
506
|
if(hasBacklink) {
|
|
@@ -546,6 +552,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
546
552
|
// convert $path to path starting at main artifact
|
|
547
553
|
function $path2path(p) {
|
|
548
554
|
const path = [];
|
|
555
|
+
/** @type {object} */
|
|
549
556
|
let env = csn;
|
|
550
557
|
for (let i = 0; p && env && i < p.length; i++) {
|
|
551
558
|
const ps = p[i];
|
|
@@ -769,7 +776,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
769
776
|
if(element._constraints._partnerCsn.cardinality.src) {
|
|
770
777
|
let srcMult = (element._constraints._partnerCsn.cardinality.src == 1) ? '0..1' : '*';
|
|
771
778
|
let newMult = (element.cardinality.max > 1) ? '*' : '0..1';
|
|
772
|
-
if(options.isV2() && srcMult
|
|
779
|
+
if(options.isV2() && srcMult !== newMult) {
|
|
773
780
|
// Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
|
|
774
781
|
warning(null, null, `Source cardinality "${element._constraints._partnerCsn.cardinality.src}" of "${element._constraints._partnerCsn._parent.name}/${element._constraints._partnerCsn.name}" conflicts with target cardinality "${element.cardinality.max}" of association "${element._parent.name}/${element.name}"`);
|
|
775
782
|
}
|
|
@@ -1268,9 +1275,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1268
1275
|
}
|
|
1269
1276
|
else if(alreadyRegistered && !alreadyRegistered.$proxy &&
|
|
1270
1277
|
!['entity', 'view'].includes(alreadyRegistered.kind)) {
|
|
1271
|
-
warning(
|
|
1272
|
-
{ name: fqProxyName, kind: alreadyRegistered.kind }
|
|
1273
|
-
'No proxy EDM entity type created due to name collision with $(NAME) of kind $(KIND)');
|
|
1278
|
+
warning('odata-definition-exists', ['definitions', element._parent.name, 'elements', element.name],
|
|
1279
|
+
{ '#': 'proxy', name: fqProxyName, kind: alreadyRegistered.kind });
|
|
1274
1280
|
return undefined;
|
|
1275
1281
|
}
|
|
1276
1282
|
}
|
|
@@ -1350,7 +1356,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1350
1356
|
// OData requires all elements along the path to be nullable: false (that is either key or notNull)
|
|
1351
1357
|
|
|
1352
1358
|
const finalType = getFinalTypeDef(eltCsn.items && eltCsn.items.type || eltCsn.type);
|
|
1353
|
-
const elements = eltCsn.elements || eltCsn.items && eltCsn.items.elements ||
|
|
1359
|
+
const elements = eltCsn.elements || eltCsn.items && eltCsn.items.elements ||
|
|
1354
1360
|
(finalType && (finalType.elements || finalType.items && finalType.items.elements));
|
|
1355
1361
|
if(elements) {
|
|
1356
1362
|
Object.entries(elements).forEach(([eltName, elt]) => {
|
|
@@ -1415,7 +1421,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1415
1421
|
if(options.isV4() && type && !isAssociationOrComposition(type) && isBuiltinType(type.type)) {
|
|
1416
1422
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1417
1423
|
const legalEdmTypes = [
|
|
1418
|
-
'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Duration',
|
|
1424
|
+
'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Duration',
|
|
1419
1425
|
'Edm.Guid', 'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte', 'Edm.String', 'Edm.TimeOfDay' ];
|
|
1420
1426
|
if(!legalEdmTypes.includes(edmType)) {
|
|
1421
1427
|
warning('odata-spec-violation-key-type', location,
|
|
@@ -1517,8 +1523,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1517
1523
|
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
1518
1524
|
// last assoc step must not be to-n and target a singleton
|
|
1519
1525
|
let p = undefined;
|
|
1520
|
-
if (!elt._target.$externalRef &&
|
|
1521
|
-
!(edmUtils.isToMany(elt) && edmUtils.isSingleton(elt._target
|
|
1526
|
+
if (!elt._target.$externalRef &&
|
|
1527
|
+
!(edmUtils.isToMany(elt) && edmUtils.isSingleton(elt._target) && options.isV4())) {
|
|
1522
1528
|
if(elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1523
1529
|
p = elt._target.$edmTgtPaths.find(p => p[0] === edmUtils.getBaseName(struct.name)) || elt._target.$edmTgtPaths[0];
|
|
1524
1530
|
}
|
|
@@ -1526,7 +1532,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1526
1532
|
const baseName = edmUtils.getBaseName(elt._target.$entitySetName || elt._target.name);
|
|
1527
1533
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1528
1534
|
// if target is in another schema, target the full qualified entity set
|
|
1529
|
-
p = (elt._target.$mySchemaName === struct.$mySchemaName) ?
|
|
1535
|
+
p = (elt._target.$mySchemaName === struct.$mySchemaName) ?
|
|
1530
1536
|
[ baseName ] : [elt._target.$mySchemaName + '.EntityContainer', baseName];
|
|
1531
1537
|
}
|
|
1532
1538
|
if(p) {
|
|
@@ -1641,7 +1647,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1641
1647
|
!isBetaEnabled(options, 'nestedServices') && serviceRootNames.forEach(sn => {
|
|
1642
1648
|
const parent = whatsMyServiceRootName(sn, false);
|
|
1643
1649
|
if(parent && parent !== sn) {
|
|
1644
|
-
|
|
1650
|
+
message( 'service-nested-service', [ 'definitions', sn ], { art: parent },
|
|
1645
1651
|
'A service can\'t be nested within a service $(ART)' );
|
|
1646
1652
|
}
|
|
1647
1653
|
});
|
|
@@ -1650,7 +1656,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1650
1656
|
if(art.kind === 'context') {
|
|
1651
1657
|
const parent = whatsMyServiceRootName(fqName);
|
|
1652
1658
|
if(parent) {
|
|
1653
|
-
|
|
1659
|
+
message( 'service-nested-context', [ 'definitions', fqName ], { art: parent },
|
|
1654
1660
|
'A context can\'t be nested within a service $(ART)' );
|
|
1655
1661
|
}
|
|
1656
1662
|
}
|
|
@@ -1788,8 +1794,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1788
1794
|
if(!navPropEntry[prop]) {
|
|
1789
1795
|
// Filter properties with prefix and reduce them into a new dictionary
|
|
1790
1796
|
const o = props.filter(p => p[0].startsWith(prefix+'.')).reduce((a,c) => {
|
|
1791
|
-
a[c[0].replace(prefix+'.', '')] = c[1];
|
|
1792
|
-
return a;
|
|
1797
|
+
a[c[0].replace(prefix+'.', '')] = c[1];
|
|
1798
|
+
return a;
|
|
1793
1799
|
}, { });
|
|
1794
1800
|
// if dictionary has entries, add them to navPropEnty
|
|
1795
1801
|
if(Object.keys(o).length) {
|
|
@@ -1925,8 +1931,9 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
1925
1931
|
// nested functions begin
|
|
1926
1932
|
function PDMSemantics()
|
|
1927
1933
|
{
|
|
1928
|
-
let dict = Object.create(null);
|
|
1929
1934
|
/*
|
|
1935
|
+
let dict = Object.create(null);
|
|
1936
|
+
|
|
1930
1937
|
dict['@PDM.xxx1'] = [ '@sap.pdm-semantics' ];
|
|
1931
1938
|
dict['@PDM.xxx2'] = [ '@sap.pdm-propery' ];
|
|
1932
1939
|
dict['@PDM.xxx3'] = [ '@sap.pdm-display-sq-no' ];
|
|
@@ -1940,7 +1947,7 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
1940
1947
|
// respect flattened annotation $value
|
|
1941
1948
|
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
1942
1949
|
*/
|
|
1943
|
-
return
|
|
1950
|
+
return Object.create(null);
|
|
1944
1951
|
}
|
|
1945
1952
|
|
|
1946
1953
|
function AnalyticalAnnotations()
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -30,18 +30,18 @@ function validateOptions(_options)
|
|
|
30
30
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const v2 = options.version.match(/v2/i)
|
|
34
|
-
const v4 = options.version.match(/v4/i)
|
|
33
|
+
const v2 = options.version.match(/v2/i) !== null;
|
|
34
|
+
const v4 = options.version.match(/v4/i) !== null;
|
|
35
35
|
|
|
36
36
|
options.v = [v2, v4];
|
|
37
37
|
options.isStructFormat = options.odataFormat && options.odataFormat === 'structured';
|
|
38
38
|
options.isFlatFormat = !options.isStructFormat;
|
|
39
39
|
|
|
40
|
-
if(options.v.filter(v=>v).length
|
|
40
|
+
if(options.v.filter(v=>v).length !== 1)
|
|
41
41
|
throw Error(`Please debug me: EDM V2:${v2}, V4:${v4}`);
|
|
42
42
|
|
|
43
|
-
options.isV2 = function() { return this.v[0]
|
|
44
|
-
options.isV4 = function() { return this.v[1]
|
|
43
|
+
options.isV2 = function() { return this.v[0]; }
|
|
44
|
+
options.isV4 = function() { return this.v[1]; }
|
|
45
45
|
|
|
46
46
|
options.pathDelimiter = options.isStructFormat ? '/' : '_';
|
|
47
47
|
|
|
@@ -115,10 +115,10 @@ function isToMany(assoc) {
|
|
|
115
115
|
return targetMax === '*' || Number(targetMax) > 1;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function isSingleton(entityCsn
|
|
118
|
+
function isSingleton(entityCsn) {
|
|
119
119
|
const singleton = entityCsn['@odata.singleton'];
|
|
120
120
|
const hasNullable = entityCsn['@odata.singleton.nullable'] !== undefined && entityCsn['@odata.singleton.nullable'] !== null;
|
|
121
|
-
return
|
|
121
|
+
return singleton || ((singleton === undefined || singleton === null) && hasNullable);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
function isEntity(artifact)
|
|
@@ -358,7 +358,7 @@ function finalizeReferentialConstraints(csn, assocCsn, options, info)
|
|
|
358
358
|
// in structured mode only resolve top level element (path rewriting is done elsewhere)
|
|
359
359
|
const depEltName = ( options.isFlatFormat ? c[0].join('_') : c[0][0] );
|
|
360
360
|
const principalEltName = ( options.isFlatFormat ? c[1].join('_') : c[1][0] );
|
|
361
|
-
const fk = (isEntity(dependentEntity) && dependentEntity.elements[ depEltName ]) ||
|
|
361
|
+
const fk = (isEntity(dependentEntity) && dependentEntity.elements[ depEltName ]) ||
|
|
362
362
|
(localDepEntity && localDepEntity.elements && localDepEntity.elements[ depEltName ]);
|
|
363
363
|
const pk = principalEntity.$keys && principalEntity.$keys[ principalEltName ];
|
|
364
364
|
if(isConstraintCandidate(fk) && isConstraintCandidate(pk)) {
|
|
@@ -463,12 +463,11 @@ function finalizeReferentialConstraints(csn, assocCsn, options, info)
|
|
|
463
463
|
* The element must never be an association or composition and be renderable.
|
|
464
464
|
*/
|
|
465
465
|
function isConstraintCandidate(elt) {
|
|
466
|
-
|
|
466
|
+
return (elt &&
|
|
467
467
|
elt.type &&
|
|
468
468
|
(!options.isFlatFormat || options.isFlatFormat && isBuiltinType(elt.type)) &&
|
|
469
469
|
!['cds.Association', 'cds.Composition'].includes(elt.type) &&
|
|
470
470
|
isEdmPropertyRendered(elt, options));
|
|
471
|
-
return rc;
|
|
472
471
|
}
|
|
473
472
|
}
|
|
474
473
|
|
|
@@ -694,10 +693,10 @@ function getBaseName(name) {
|
|
|
694
693
|
|
|
695
694
|
// This is a poor mans path resolver for $self partner paths only
|
|
696
695
|
function resolveOriginAssoc(csn, env, path) {
|
|
697
|
-
for(
|
|
696
|
+
for(const segment of path) {
|
|
698
697
|
let elements = (env.items && env.items.elements || env.elements);
|
|
699
698
|
if(elements)
|
|
700
|
-
env = env.elements[
|
|
699
|
+
env = env.elements[segment];
|
|
701
700
|
let type = (env.items && env.items.type || env.type);
|
|
702
701
|
if(type && !isBuiltinType(type) && !(env.items && env.items.elements || env.elements))
|
|
703
702
|
env = csn.definitions[env.type];
|