@sap/cds-compiler 2.11.2 → 2.13.6
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 +175 -2
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +23 -17
- package/bin/cdsse.js +2 -2
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +32 -79
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +2 -1
- package/lib/backends.js +16 -26
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +10 -19
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +75 -9
- package/lib/base/messages.js +31 -35
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/unknownMagic.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +12 -8
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +42 -21
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +46 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +100 -65
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +215 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +189 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +144 -53
- package/lib/language/language.g4 +424 -200
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +550 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +322 -198
- package/lib/model/csnUtils.js +226 -370
- package/lib/model/enrichCsn.js +124 -69
- package/lib/model/revealInternalProperties.js +29 -7
- package/lib/model/sortViews.js +10 -2
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +8 -3
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +203 -122
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +161 -82
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +212 -0
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +556 -106
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +103 -28
- package/lib/transform/db/views.js +92 -44
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +98 -783
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +13 -30
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +8 -3
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2349
- package/lib/compiler/resolver.js +0 -2922
- package/lib/transform/db/helpers.js +0 -58
- package/lib/transform/universalCsnEnricher.js +0 -67
|
@@ -39,23 +39,27 @@ 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 {
|
|
46
|
+
inspectRef,
|
|
46
47
|
getCsnDef,
|
|
47
48
|
getFinalTypeDef,
|
|
48
49
|
isStructured,
|
|
49
50
|
isAssocOrComposition,
|
|
50
51
|
} = getUtils(csn);
|
|
51
52
|
|
|
53
|
+
// proxies are merged into the final model after all proxy elements are collected
|
|
54
|
+
const proxyCache = [];
|
|
52
55
|
|
|
53
56
|
// make sure options are complete
|
|
54
57
|
let options = validateOptions(_options);
|
|
55
58
|
|
|
56
59
|
// Fetch service definitions
|
|
57
|
-
const
|
|
58
|
-
|
|
60
|
+
const allDefs = Object.entries(csn.definitions||{});
|
|
61
|
+
|
|
62
|
+
const serviceRoots = allDefs.reduce((serviceRoots, [artName, art]) => {
|
|
59
63
|
if(art.kind === 'service') {
|
|
60
64
|
serviceRoots[artName] = Object.assign(art, { name: artName });
|
|
61
65
|
}
|
|
@@ -64,11 +68,24 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
64
68
|
|
|
65
69
|
// first of all we need to know about all 'real' user defined services
|
|
66
70
|
const serviceRootNames = Object.keys(serviceRoots).sort((a,b)=>b.length-a.length);
|
|
71
|
+
|
|
67
72
|
function whatsMyServiceRootName(n, self=true) {
|
|
68
73
|
return serviceRootNames.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') || (n === sn && self) ? sn : rc, undefined);
|
|
69
74
|
}
|
|
75
|
+
|
|
76
|
+
// find a globally unambiguous schema name to collect all top level 'root' types
|
|
77
|
+
// TODO: work on service basis (this requires post exposure renaming)
|
|
78
|
+
let autoexposeSchemaName = 'root';
|
|
79
|
+
let i = 1;
|
|
80
|
+
while (allDefs.some(([artName, _art]) => {
|
|
81
|
+
const p = artName.split('.');
|
|
82
|
+
return p.length === 2 && p[0] === autoexposeSchemaName;
|
|
83
|
+
})) {
|
|
84
|
+
autoexposeSchemaName = 'root' + i++;
|
|
85
|
+
}
|
|
86
|
+
|
|
70
87
|
if(serviceRootNames.length === 0) {
|
|
71
|
-
return [serviceRoots, Object.create(null), whatsMyServiceRootName, options];
|
|
88
|
+
return [serviceRoots, Object.create(null), whatsMyServiceRootName, autoexposeSchemaName, options];
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
// Structural CSN inbound QA checks
|
|
@@ -103,7 +120,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
103
120
|
service), the namespace (including (sub-)contexts) or as last resort (if the type name
|
|
104
121
|
has no prefix path) a 'root' namespace.
|
|
105
122
|
*/
|
|
106
|
-
const schemas = typesExposure(csn, whatsMyServiceRootName, options, csnUtils, { error });
|
|
123
|
+
const schemas = typesExposure(csn, whatsMyServiceRootName, autoexposeSchemaName, options, csnUtils, { error });
|
|
107
124
|
|
|
108
125
|
// First attach names to all definitions (and actions/params) in the model
|
|
109
126
|
// elements are done in initializeStruct
|
|
@@ -145,6 +162,9 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
145
162
|
// create association target proxies
|
|
146
163
|
// Decide if an entity set needs to be constructed or not
|
|
147
164
|
forEachDefinition(csn, [ exposeTargetsAsProxiesOrSchemaRefs, determineEntitySet ]);
|
|
165
|
+
// finalize proxy creation
|
|
166
|
+
mergeProxiesIntoModel();
|
|
167
|
+
|
|
148
168
|
if(options.isV4())
|
|
149
169
|
forEachDefinition(csn, initializeEdmNavPropBindingTargets);
|
|
150
170
|
|
|
@@ -156,7 +176,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
156
176
|
forEachDefinition(csn, [ initializeEdmKeyRefPaths, initializeEdmNavPropBindingPaths,
|
|
157
177
|
initializeEdmTypesAndDescription, checkArtifactIdentifierAndBoundActions ]);
|
|
158
178
|
}
|
|
159
|
-
return [serviceRoots, schemas, whatsMyServiceRootName, options];
|
|
179
|
+
return [serviceRoots, schemas, whatsMyServiceRootName, autoexposeSchemaName, options];
|
|
160
180
|
|
|
161
181
|
//////////////////////////////////////////////////////////////////////
|
|
162
182
|
//
|
|
@@ -186,16 +206,16 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
186
206
|
const dotEntityNameMap = Object.create(null);
|
|
187
207
|
const dotTypeNameMap = Object.create(null);
|
|
188
208
|
forEachDefinition(csn, (def, defName) => {
|
|
189
|
-
if(['entity', '
|
|
209
|
+
if(['entity', 'type', 'action', 'function'].includes(def.kind)) {
|
|
190
210
|
const rootDef = getRootDef(defName);
|
|
191
211
|
// if this definition has a root def and the root def is not the service/schema name
|
|
192
212
|
// => service C { type D.E }, replace the prefix dots with underscores
|
|
193
213
|
if(rootDef && defName !== rootDef && rootDef !== getSchemaPrefix(defName)) {
|
|
194
214
|
let newDefName = rootDef + '.' + defName.replace(rootDef + '.', '').replace(/\./g, '_');
|
|
195
215
|
// store renamed types in correlation maps for later renaming
|
|
196
|
-
if(
|
|
216
|
+
if(def.kind === 'entity')
|
|
197
217
|
dotEntityNameMap[defName] = newDefName;
|
|
198
|
-
if(
|
|
218
|
+
if(def.kind === 'type')
|
|
199
219
|
dotTypeNameMap[defName] = newDefName;
|
|
200
220
|
// rename in csn.definitions
|
|
201
221
|
const art = csn.definitions[newDefName];
|
|
@@ -252,7 +272,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
252
272
|
*/
|
|
253
273
|
function splitDottedDefinitionsIntoSeparateServices() {
|
|
254
274
|
forEachDefinition(csn, (def, defName) => {
|
|
255
|
-
if(
|
|
275
|
+
if(def.kind !== 'service') {
|
|
256
276
|
const myServiceRoot = whatsMyServiceRootName(defName);
|
|
257
277
|
const mySchemaPrefix = getSchemaPrefix(defName);
|
|
258
278
|
if(myServiceRoot && options.isV4() &&
|
|
@@ -342,7 +362,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
342
362
|
// entity set. Instead try to rewrite the annotation in such a way that it is effective
|
|
343
363
|
// on the containment navigation property.
|
|
344
364
|
function initializeContainments(container) {
|
|
345
|
-
if(
|
|
365
|
+
if(container.kind === 'entity') {
|
|
346
366
|
forEachMemberRecursively(container, initContainments,
|
|
347
367
|
[], true, { elementsOnly: true });
|
|
348
368
|
}
|
|
@@ -455,8 +475,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
455
475
|
// propagate containment information, if containment is recursive, use parameterCsn.name as _containerEntity
|
|
456
476
|
if(entityCsn._containerEntity) {
|
|
457
477
|
setProp(parameterCsn, '_containerEntity', []);
|
|
458
|
-
for(
|
|
459
|
-
parameterCsn._containerEntity.push((c
|
|
478
|
+
for(const c of entityCsn._containerEntity) {
|
|
479
|
+
parameterCsn._containerEntity.push((c === entityCsn.name) ? parameterCsn.name : c);
|
|
460
480
|
}
|
|
461
481
|
}
|
|
462
482
|
entityCsn._containerEntity = [ parameterCsn ];
|
|
@@ -466,6 +486,17 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
466
486
|
elt.name = n;
|
|
467
487
|
delete elt.kind;
|
|
468
488
|
elt.key = true; // params become primary key in parameter entity
|
|
489
|
+
/*
|
|
490
|
+
Spec meeting decision 28.02.22:
|
|
491
|
+
Annotation @sap.parameter allows two values "mandatory"/"optional".
|
|
492
|
+
Question was how to deal with incompatible "optional".
|
|
493
|
+
Only "mandatory" is allowed because in RAP all parameters are NOT NULL
|
|
494
|
+
and so they are in CAP (all view parameters become primary keys which are not null).
|
|
495
|
+
*/
|
|
496
|
+
if(options.isV2())
|
|
497
|
+
assignAnnotation(elt, '@sap.parameter', 'mandatory');
|
|
498
|
+
else
|
|
499
|
+
assignAnnotation(elt, '@Common.FieldControl', { '#': 'Mandatory' });
|
|
469
500
|
parameterCsn.elements[n] = elt;
|
|
470
501
|
});
|
|
471
502
|
linkAssociationTarget(parameterCsn);
|
|
@@ -489,12 +520,18 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
489
520
|
member.$path[1] = parameterEntityName;
|
|
490
521
|
});
|
|
491
522
|
|
|
492
|
-
|
|
493
|
-
|
|
523
|
+
if(csn.definitions[parameterCsn.name])
|
|
524
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
|
|
525
|
+
else
|
|
526
|
+
csn.definitions[parameterCsn.name] = parameterCsn;
|
|
494
527
|
// modify the original parameter entity with backlink and new name
|
|
495
|
-
csn.definitions[originalEntityName]
|
|
496
|
-
|
|
497
|
-
|
|
528
|
+
if(csn.definitions[originalEntityName])
|
|
529
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: originalEntityName });
|
|
530
|
+
else {
|
|
531
|
+
csn.definitions[originalEntityName] = entityCsn;
|
|
532
|
+
delete csn.definitions[entityCsn.name];
|
|
533
|
+
entityCsn.name = originalEntityName;
|
|
534
|
+
}
|
|
498
535
|
setProp(entityCsn, '$entitySetName', originalEntitySetName);
|
|
499
536
|
// add backlink association
|
|
500
537
|
if(hasBacklink) {
|
|
@@ -546,6 +583,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
546
583
|
// convert $path to path starting at main artifact
|
|
547
584
|
function $path2path(p) {
|
|
548
585
|
const path = [];
|
|
586
|
+
/** @type {object} */
|
|
549
587
|
let env = csn;
|
|
550
588
|
for (let i = 0; p && env && i < p.length; i++) {
|
|
551
589
|
const ps = p[i];
|
|
@@ -574,16 +612,16 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
574
612
|
|
|
575
613
|
// Iterate all struct elements
|
|
576
614
|
forEachMemberRecursively(def.items || def, (element, elementName, prop, path = [], construct) => {
|
|
577
|
-
if(
|
|
615
|
+
if(prop !== 'elements')
|
|
578
616
|
return;
|
|
579
617
|
|
|
580
618
|
initElement(element, elementName, construct);
|
|
581
619
|
|
|
582
|
-
if(
|
|
620
|
+
if(def.kind !== 'event' && def.kind !== 'aspect') {
|
|
583
621
|
if(element._parent && element._parent.$mySchemaName) {
|
|
584
622
|
if(!isODataSimpleIdentifier(elementName)) {
|
|
585
623
|
signalIllegalIdentifier(elementName, ['definitions', def.name].concat(path));
|
|
586
|
-
} else if (options.isV2() && /^(_|[0-9])/.test(elementName) &&
|
|
624
|
+
} else if (options.isV2() && /^(_|[0-9])/.test(elementName) && element._parent.kind === 'entity') {
|
|
587
625
|
// FIXME: Rewrite signalIllegalIdentifier function to be more flexible
|
|
588
626
|
error(null, ['definitions', def.name, 'elements', elementName], { prop: elementName[0] },
|
|
589
627
|
'Element names must not start with $(PROP) for OData V2');
|
|
@@ -769,7 +807,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
769
807
|
if(element._constraints._partnerCsn.cardinality.src) {
|
|
770
808
|
let srcMult = (element._constraints._partnerCsn.cardinality.src == 1) ? '0..1' : '*';
|
|
771
809
|
let newMult = (element.cardinality.max > 1) ? '*' : '0..1';
|
|
772
|
-
if(options.isV2() && srcMult
|
|
810
|
+
if(options.isV2() && srcMult !== newMult) {
|
|
773
811
|
// Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
|
|
774
812
|
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
813
|
}
|
|
@@ -838,7 +876,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
838
876
|
proxies.
|
|
839
877
|
*/
|
|
840
878
|
function exposeTargetsAsProxiesOrSchemaRefs(struct) {
|
|
841
|
-
if(
|
|
879
|
+
if(struct.kind === 'context' || struct.kind === 'service' || struct.$proxy)
|
|
842
880
|
return;
|
|
843
881
|
|
|
844
882
|
// globalSchemaPrefix is the prefix for all proxy registrations and must not change
|
|
@@ -873,6 +911,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
873
911
|
// If the target is in another schema, check if both the source and the target share the same service name.
|
|
874
912
|
// If they share the same service name, then it is just a cross schema navigation within the same EDM, no
|
|
875
913
|
// proxy required.
|
|
914
|
+
// association must be managed and not unmanaged
|
|
876
915
|
|
|
877
916
|
// odataProxies (P) and odataXServiceRefs (X) are evalutated as follows:
|
|
878
917
|
// P | X | Action
|
|
@@ -883,7 +922,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
883
922
|
|
|
884
923
|
const targetSchemaName = element._target.$mySchemaName;
|
|
885
924
|
if(isProxyRequired(element)) {
|
|
886
|
-
if(options.isV4() &&
|
|
925
|
+
if(options.isV4() &&
|
|
926
|
+
(options.toOdata.odataProxies || options.toOdata.odataXServiceRefs)
|
|
927
|
+
// must be a managed association with keys and no ON condition
|
|
928
|
+
/* && element.keys && !element.on */) {
|
|
887
929
|
// reuse proxy if available
|
|
888
930
|
let proxy = getProxyForTargetOf(element);
|
|
889
931
|
if(!proxy) {
|
|
@@ -902,6 +944,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
902
944
|
element._constraints.constraints = Object.create(null);
|
|
903
945
|
if(proxy.kind === 'entity') {
|
|
904
946
|
element._target = proxy;
|
|
947
|
+
populateProxyElements(element, proxy, getForeignKeyDefinitions(element, ['definitions', struct.name, 'elements', element.name]));
|
|
905
948
|
}
|
|
906
949
|
else {
|
|
907
950
|
// fake the target to be proxy
|
|
@@ -926,7 +969,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
926
969
|
|
|
927
970
|
function noNavPropMsg(elt) {
|
|
928
971
|
warning(null, ['definitions', struct.name, 'elements', elt.name],
|
|
929
|
-
{ target: elt._target.name }, 'No OData navigation property generated, target $(TARGET) is outside
|
|
972
|
+
{ target: elt._target.name, service: globalSchemaPrefix }, 'No OData navigation property generated, target $(TARGET) is outside of service $(SERVICE)');
|
|
930
973
|
}
|
|
931
974
|
|
|
932
975
|
function createSchemaRefFor(assoc, targetSchemaName) {
|
|
@@ -947,10 +990,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
947
990
|
// 1) construct the proxy definition
|
|
948
991
|
// proxyShortName: strip the serviceName and replace '.' with '_'
|
|
949
992
|
const proxyShortName = assoc._target.name.replace(proxySchemaName + '.', '').replace(/\./g, '_');
|
|
950
|
-
|
|
951
|
-
const
|
|
952
|
-
const proxy = { name: fullName, kind: 'entity', $proxy: true, elements: Object.create(null) };
|
|
993
|
+
// fullName: Prepend serviceName and if in same service add '_proxy'
|
|
994
|
+
const proxy = { name: proxySchemaName + '.' + proxyShortName, kind: 'entity', $proxy: true, elements: Object.create(null) };
|
|
953
995
|
setProp(proxy, '$mySchemaName', proxySchemaName);
|
|
996
|
+
setProp(proxy, '$proxyShortName', proxyShortName);
|
|
954
997
|
setProp(proxy, '$keys', Object.create(null));
|
|
955
998
|
setProp(proxy, '$hasEntitySet', false);
|
|
956
999
|
setProp(proxy, '$exposedTypes', Object.create(null));
|
|
@@ -961,59 +1004,67 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
961
1004
|
});
|
|
962
1005
|
|
|
963
1006
|
// 2) create the elements and $keys
|
|
964
|
-
populateProxyElements(proxy, assoc._target.$keys);
|
|
965
|
-
// 3) sort the exposed types so that they appear lexicographically ordered in the EDM
|
|
966
|
-
proxy.$exposedTypes = Object.keys(proxy.$exposedTypes).sort().reduce((dict, tn) => {
|
|
967
|
-
dict[tn] = proxy.$exposedTypes[tn];
|
|
968
|
-
return dict
|
|
969
|
-
}, Object.create(null));
|
|
970
|
-
|
|
1007
|
+
populateProxyElements(assoc, proxy, assoc._target.$keys);
|
|
971
1008
|
return proxy;
|
|
972
1009
|
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// return array of foreign key defs in the target
|
|
1013
|
+
function getForeignKeyDefinitions(e, path=undefined) {
|
|
1014
|
+
// populate foreign keys, if a key can't be resolved -> parametertype redirects, ignore them
|
|
1015
|
+
return e.keys ? e.keys.map((_fk, i) => {
|
|
1016
|
+
return inspectRef([...(path || e.$path), 'keys', i]).art;
|
|
1017
|
+
}).filter(fk=>fk) : [];
|
|
1018
|
+
}
|
|
973
1019
|
// copy over the primary keys of the target and trigger the type exposure
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1020
|
+
function populateProxyElements(assoc, proxy, elements) {
|
|
1021
|
+
forAll(elements, e => {
|
|
1022
|
+
if (isEdmPropertyRendered(e, options) && !proxy.elements[e.name]) {
|
|
1023
|
+
let newElt = undefined;
|
|
1024
|
+
if(isAssocOrComposition(e.type)) {
|
|
1025
|
+
if(!e.on && e.keys) {
|
|
1026
|
+
if(options.toOdata.odataNoTransitiveProxies)
|
|
1027
|
+
newElt = convertManagedAssocIntoStruct(e);
|
|
1028
|
+
else
|
|
983
1029
|
newElt = createProxyOrSchemaRefForManagedAssoc(e);
|
|
984
|
-
}
|
|
985
|
-
else {
|
|
986
|
-
info(null, ['definitions', struct.name, 'elements', assoc.name],
|
|
987
|
-
{ name: fullName, target: assoc._target.name },
|
|
988
|
-
'Unmanaged associations are not supported as primary keys for proxy entity type $(NAME) of unexposed association target $(TARGET)');
|
|
989
|
-
}
|
|
990
1030
|
}
|
|
991
1031
|
else {
|
|
992
|
-
|
|
1032
|
+
info(null, ['definitions', struct.name, 'elements', assoc.name],
|
|
1033
|
+
{ name: proxy.nname, target: assoc._target.name },
|
|
1034
|
+
'Unmanaged associations are not supported as primary keys for proxy entity type $(NAME) of unexposed association target $(TARGET)');
|
|
993
1035
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
newElt = cloneCsn(e, options);
|
|
1039
|
+
}
|
|
1040
|
+
if(newElt) {
|
|
1041
|
+
initElement(newElt, e.name, proxy);
|
|
1042
|
+
if(isStructured(newElt)) {
|
|
997
1043
|
// argument proxySchemaName forces an anonymous type definition for newElt into the
|
|
998
1044
|
// proxy schema. If omitted, this exposure defaults to 'root', in case API flavor of the day
|
|
999
1045
|
// changes...
|
|
1000
|
-
|
|
1046
|
+
exposeStructTypeForProxyOf(proxy, newElt, proxy.$proxyShortName + '_' + newElt.name, proxy.$mySchemaName);
|
|
1001
1047
|
// elements of newElt are required for key ref paths
|
|
1002
|
-
}
|
|
1003
|
-
// all elements must become primary key
|
|
1004
|
-
proxy.$keys[e.name] = proxy.elements[newElt.name] = newElt;
|
|
1005
1048
|
}
|
|
1049
|
+
proxy.elements[newElt.name] = newElt;
|
|
1050
|
+
if(newElt.key)
|
|
1051
|
+
proxy.$keys[newElt.name] = newElt;
|
|
1006
1052
|
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
// 3) sort the exposed types so that they appear lexicographically ordered in the EDM
|
|
1056
|
+
proxy.$exposedTypes = Object.keys(proxy.$exposedTypes).sort().reduce((dict, tn) => {
|
|
1057
|
+
dict[tn] = proxy.$exposedTypes[tn];
|
|
1058
|
+
return dict
|
|
1059
|
+
}, Object.create(null));
|
|
1009
1060
|
|
|
1010
1061
|
// If 'node' exists and has a structured type that is not exposed in 'service', (because the type is
|
|
1011
1062
|
// anonymous or has a definition outside of 'service'), create an equivalent type in 'service', either
|
|
1012
1063
|
// using the type's name or (if anonymous) 'artificialName', and make 'node' use that type instead.
|
|
1013
1064
|
// Complain if there is an error.
|
|
1014
|
-
function exposeStructTypeForProxyOf(proxy, node, artificialName, typeSchemaName=
|
|
1065
|
+
function exposeStructTypeForProxyOf(proxy, node, artificialName, typeSchemaName=autoexposeSchemaName) {
|
|
1015
1066
|
const isNotInProtNS = node.type ? !isBuiltinType(node.type) : true;
|
|
1016
|
-
// Always expose types referred to by a proxy, never reuse an eventually
|
|
1067
|
+
// Always expose types referred to by a proxy, never reuse an eventually existing type
|
|
1017
1068
|
// as the nested elements must all be not nullable
|
|
1018
1069
|
if (isNotInProtNS) {
|
|
1019
1070
|
let typeDef = node.type ? csn.definitions[node.type] : /* anonymous type */ node;
|
|
@@ -1045,7 +1096,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1045
1096
|
if(typeClone) {
|
|
1046
1097
|
// Recurse into elements of 'type' (if any)
|
|
1047
1098
|
typeClone.elements && Object.entries(typeClone.elements).forEach( ([elemName, elem]) => {
|
|
1048
|
-
// if this is a foreign key elment, we must check
|
|
1099
|
+
// if this is a foreign key elment, we must check whether or not the association
|
|
1049
1100
|
// has been exposed as proxy. If it has not been exposed, no further structured
|
|
1050
1101
|
// types must be exposed as 'Proxy_' types.
|
|
1051
1102
|
|
|
@@ -1102,7 +1153,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1102
1153
|
Object.keys(elem).forEach(prop => type.elements[elemName][prop] = elem[prop])
|
|
1103
1154
|
type.elements[elemName].notNull = true;
|
|
1104
1155
|
}
|
|
1105
|
-
else {
|
|
1156
|
+
else if(elem.keys && !elem.on) {
|
|
1157
|
+
// a primary key can never be an unmanaged association
|
|
1106
1158
|
type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
|
|
1107
1159
|
}
|
|
1108
1160
|
setProp(type.elements[elemName], 'name', elem.name);
|
|
@@ -1159,6 +1211,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1159
1211
|
}
|
|
1160
1212
|
else if(options.toOdata.odataProxies) {
|
|
1161
1213
|
proxy = createProxyFor(e, e._target.$mySchemaName);
|
|
1214
|
+
populateProxyElements(e, proxy, getForeignKeyDefinitions(e));
|
|
1162
1215
|
}
|
|
1163
1216
|
proxy = registerProxy(proxy, e);
|
|
1164
1217
|
}
|
|
@@ -1222,8 +1275,11 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1222
1275
|
function registerProxy(proxy, element) {
|
|
1223
1276
|
if(proxy === undefined)
|
|
1224
1277
|
return undefined;
|
|
1225
|
-
|
|
1226
|
-
|
|
1278
|
+
|
|
1279
|
+
setProp(proxy, '$globalSchemaPrefix', globalSchemaPrefix);
|
|
1280
|
+
setProp(proxy, '$origin', element);
|
|
1281
|
+
|
|
1282
|
+
const fqProxyName = proxy.$globalSchemaPrefix + '.' + proxy.name;
|
|
1227
1283
|
|
|
1228
1284
|
if(!element._target.$cachedProxy)
|
|
1229
1285
|
assignProp(element._target, '$cachedProxy', Object.create(null));
|
|
@@ -1231,8 +1287,19 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1231
1287
|
info(null, ['definitions', struct.name, 'elements', element.name],
|
|
1232
1288
|
{ name: fqProxyName }, 'Proxy EDM entity type $(NAME) has already been registered');
|
|
1233
1289
|
}
|
|
1234
|
-
else
|
|
1290
|
+
else {
|
|
1291
|
+
determineEntitySet(proxy);
|
|
1292
|
+
proxyCache.push(proxy);
|
|
1235
1293
|
element._target.$cachedProxy[globalSchemaPrefix] = proxy;
|
|
1294
|
+
}
|
|
1295
|
+
return proxy;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
function mergeProxiesIntoModel() {
|
|
1300
|
+
proxyCache.forEach(proxy => {
|
|
1301
|
+
const fqProxyName = proxy.$globalSchemaPrefix + '.' + proxy.name;
|
|
1302
|
+
const fqSchemaName = proxy.$globalSchemaPrefix + '.' + proxy.$mySchemaName;
|
|
1236
1303
|
|
|
1237
1304
|
if(proxy.kind === 'entity') {
|
|
1238
1305
|
// collect all schemas even for newly exposed types
|
|
@@ -1244,7 +1311,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1244
1311
|
// don't forget to prepend the global namespace prefix
|
|
1245
1312
|
// schemas are ordered in csn2edm.js for each service
|
|
1246
1313
|
Object.keys(proxy.$exposedTypes).forEach(t =>
|
|
1247
|
-
schemaSet.add(globalSchemaPrefix + '.' + getSchemaPrefix(t)));
|
|
1314
|
+
schemaSet.add(proxy.$globalSchemaPrefix + '.' + getSchemaPrefix(t)));
|
|
1248
1315
|
schemaSet.forEach(schemaName => {
|
|
1249
1316
|
if(!schemas[schemaName]) {
|
|
1250
1317
|
schemas[schemaName] = { kind: 'schema', name: schemaName };
|
|
@@ -1252,26 +1319,29 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1252
1319
|
}
|
|
1253
1320
|
});
|
|
1254
1321
|
/** @type {object} */
|
|
1255
|
-
const alreadyRegistered = csn.definitions[fqProxyName]
|
|
1322
|
+
const alreadyRegistered = csn.definitions[fqProxyName];
|
|
1256
1323
|
if(!alreadyRegistered) {
|
|
1257
1324
|
csn.definitions[fqProxyName] = proxy;
|
|
1258
1325
|
setProp(proxy, '$path', ['definitions', fqProxyName]);
|
|
1259
1326
|
Object.entries(proxy.$exposedTypes).forEach(([tn, v]) => {
|
|
1260
|
-
const fqtn = globalSchemaPrefix + '.' + tn;
|
|
1327
|
+
const fqtn = proxy.$globalSchemaPrefix + '.' + tn;
|
|
1261
1328
|
if(csn.definitions[fqtn] === undefined) {
|
|
1262
1329
|
csn.definitions[fqtn] = v;
|
|
1263
1330
|
setProp(v, '$path', ['definitions', fqtn]);
|
|
1264
1331
|
}
|
|
1265
1332
|
});
|
|
1266
|
-
|
|
1333
|
+
// default location is not always correct in case proxy has been created by a nested assoc
|
|
1334
|
+
// as foreign key targeting another proxy association
|
|
1335
|
+
let loc = ['definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name];
|
|
1336
|
+
if(proxy.$origin._parent.$path)
|
|
1337
|
+
loc = [...proxy.$origin._parent.$path, 'elements', proxy.$origin.name]
|
|
1338
|
+
info(null, loc,
|
|
1267
1339
|
{ name: proxy.name }, 'Created proxy EDM entity type $(NAME)');
|
|
1268
1340
|
}
|
|
1269
1341
|
else if(alreadyRegistered && !alreadyRegistered.$proxy &&
|
|
1270
|
-
|
|
1271
|
-
warning(
|
|
1272
|
-
{ name: fqProxyName, kind: alreadyRegistered.kind }
|
|
1273
|
-
'No proxy EDM entity type created due to name collision with $(NAME) of kind $(KIND)');
|
|
1274
|
-
return undefined;
|
|
1342
|
+
alreadyRegistered.kind !== 'entity') {
|
|
1343
|
+
warning('odata-definition-exists', ['definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name],
|
|
1344
|
+
{ '#': 'proxy', name: fqProxyName, kind: alreadyRegistered.kind });
|
|
1275
1345
|
}
|
|
1276
1346
|
}
|
|
1277
1347
|
else {
|
|
@@ -1279,15 +1349,14 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1279
1349
|
if(!schemas[fqSchemaName]) {
|
|
1280
1350
|
schemas[fqSchemaName] = proxy;
|
|
1281
1351
|
schemaNames.push(fqSchemaName);
|
|
1282
|
-
info(null, ['definitions',
|
|
1352
|
+
info(null, ['definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name],
|
|
1283
1353
|
{ name: proxy.name }, 'Created EDM namespace reference $(NAME)');
|
|
1284
1354
|
}
|
|
1285
1355
|
// don't error on duplicate schemas, if it's already present then all is good....
|
|
1286
1356
|
}
|
|
1357
|
+
});
|
|
1287
1358
|
// sort the global schemaNames array
|
|
1288
|
-
|
|
1289
|
-
return proxy;
|
|
1290
|
-
}
|
|
1359
|
+
schemaNames.sort((a,b) => b.length-a.length);
|
|
1291
1360
|
}
|
|
1292
1361
|
|
|
1293
1362
|
/*
|
|
@@ -1350,7 +1419,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1350
1419
|
// OData requires all elements along the path to be nullable: false (that is either key or notNull)
|
|
1351
1420
|
|
|
1352
1421
|
const finalType = getFinalTypeDef(eltCsn.items && eltCsn.items.type || eltCsn.type);
|
|
1353
|
-
const elements = eltCsn.elements || eltCsn.items && eltCsn.items.elements ||
|
|
1422
|
+
const elements = eltCsn.elements || eltCsn.items && eltCsn.items.elements ||
|
|
1354
1423
|
(finalType && (finalType.elements || finalType.items && finalType.items.elements));
|
|
1355
1424
|
if(elements) {
|
|
1356
1425
|
Object.entries(elements).forEach(([eltName, elt]) => {
|
|
@@ -1415,7 +1484,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1415
1484
|
if(options.isV4() && type && !isAssociationOrComposition(type) && isBuiltinType(type.type)) {
|
|
1416
1485
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1417
1486
|
const legalEdmTypes = [
|
|
1418
|
-
'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Duration',
|
|
1487
|
+
'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Duration',
|
|
1419
1488
|
'Edm.Guid', 'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte', 'Edm.String', 'Edm.TimeOfDay' ];
|
|
1420
1489
|
if(!legalEdmTypes.includes(edmType)) {
|
|
1421
1490
|
warning('odata-spec-violation-key-type', location,
|
|
@@ -1517,8 +1586,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1517
1586
|
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
1518
1587
|
// last assoc step must not be to-n and target a singleton
|
|
1519
1588
|
let p = undefined;
|
|
1520
|
-
if (!elt._target.$externalRef &&
|
|
1521
|
-
!(edmUtils.isToMany(elt) && edmUtils.isSingleton(elt._target
|
|
1589
|
+
if (!elt._target.$externalRef &&
|
|
1590
|
+
!(edmUtils.isToMany(elt) && edmUtils.isSingleton(elt._target) && options.isV4())) {
|
|
1522
1591
|
if(elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1523
1592
|
p = elt._target.$edmTgtPaths.find(p => p[0] === edmUtils.getBaseName(struct.name)) || elt._target.$edmTgtPaths[0];
|
|
1524
1593
|
}
|
|
@@ -1526,7 +1595,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1526
1595
|
const baseName = edmUtils.getBaseName(elt._target.$entitySetName || elt._target.name);
|
|
1527
1596
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1528
1597
|
// if target is in another schema, target the full qualified entity set
|
|
1529
|
-
p = (elt._target.$mySchemaName === struct.$mySchemaName) ?
|
|
1598
|
+
p = (elt._target.$mySchemaName === struct.$mySchemaName) ?
|
|
1530
1599
|
[ baseName ] : [elt._target.$mySchemaName + '.EntityContainer', baseName];
|
|
1531
1600
|
}
|
|
1532
1601
|
if(p) {
|
|
@@ -1641,7 +1710,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1641
1710
|
!isBetaEnabled(options, 'nestedServices') && serviceRootNames.forEach(sn => {
|
|
1642
1711
|
const parent = whatsMyServiceRootName(sn, false);
|
|
1643
1712
|
if(parent && parent !== sn) {
|
|
1644
|
-
|
|
1713
|
+
message( 'service-nested-service', [ 'definitions', sn ], { art: parent },
|
|
1645
1714
|
'A service can\'t be nested within a service $(ART)' );
|
|
1646
1715
|
}
|
|
1647
1716
|
});
|
|
@@ -1650,7 +1719,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1650
1719
|
if(art.kind === 'context') {
|
|
1651
1720
|
const parent = whatsMyServiceRootName(fqName);
|
|
1652
1721
|
if(parent) {
|
|
1653
|
-
|
|
1722
|
+
message( 'service-nested-context', [ 'definitions', fqName ], { art: parent },
|
|
1654
1723
|
'A context can\'t be nested within a service $(ART)' );
|
|
1655
1724
|
}
|
|
1656
1725
|
}
|
|
@@ -1731,12 +1800,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1731
1800
|
// serviceRef.push('');
|
|
1732
1801
|
if(serviceRef[serviceRef.length-1] !== '$metadata')
|
|
1733
1802
|
serviceRef.push('$metadata');
|
|
1734
|
-
|
|
1803
|
+
let sc = { kind: 'reference',
|
|
1735
1804
|
name: targetSchemaName,
|
|
1736
1805
|
ref: { Uri: serviceRef.join('/') },
|
|
1737
|
-
inc: { Namespace: targetSchemaName }
|
|
1738
|
-
$mySchemaName: targetSchemaName,
|
|
1806
|
+
inc: { Namespace: targetSchemaName }
|
|
1739
1807
|
};
|
|
1808
|
+
setProp(sc, '$mySchemaName', targetSchemaName);
|
|
1809
|
+
return sc;
|
|
1740
1810
|
|
|
1741
1811
|
/**
|
|
1742
1812
|
* Resolve a service endpoint path to mount it to as follows...
|
|
@@ -1788,8 +1858,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1788
1858
|
if(!navPropEntry[prop]) {
|
|
1789
1859
|
// Filter properties with prefix and reduce them into a new dictionary
|
|
1790
1860
|
const o = props.filter(p => p[0].startsWith(prefix+'.')).reduce((a,c) => {
|
|
1791
|
-
a[c[0].replace(prefix+'.', '')] = c[1];
|
|
1792
|
-
return a;
|
|
1861
|
+
a[c[0].replace(prefix+'.', '')] = c[1];
|
|
1862
|
+
return a;
|
|
1793
1863
|
}, { });
|
|
1794
1864
|
// if dictionary has entries, add them to navPropEnty
|
|
1795
1865
|
if(Object.keys(o).length) {
|
|
@@ -1925,8 +1995,9 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
1925
1995
|
// nested functions begin
|
|
1926
1996
|
function PDMSemantics()
|
|
1927
1997
|
{
|
|
1928
|
-
let dict = Object.create(null);
|
|
1929
1998
|
/*
|
|
1999
|
+
let dict = Object.create(null);
|
|
2000
|
+
|
|
1930
2001
|
dict['@PDM.xxx1'] = [ '@sap.pdm-semantics' ];
|
|
1931
2002
|
dict['@PDM.xxx2'] = [ '@sap.pdm-propery' ];
|
|
1932
2003
|
dict['@PDM.xxx3'] = [ '@sap.pdm-display-sq-no' ];
|
|
@@ -1940,7 +2011,7 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
1940
2011
|
// respect flattened annotation $value
|
|
1941
2012
|
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
1942
2013
|
*/
|
|
1943
|
-
return
|
|
2014
|
+
return Object.create(null);
|
|
1944
2015
|
}
|
|
1945
2016
|
|
|
1946
2017
|
function AnalyticalAnnotations()
|
|
@@ -2151,7 +2222,7 @@ function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
|
2151
2222
|
}
|
|
2152
2223
|
|
|
2153
2224
|
function checkSemantics(struct, propName, propValue) {
|
|
2154
|
-
if(
|
|
2225
|
+
if(propValue === 'timeseries' || propValue === 'aggregate') {
|
|
2155
2226
|
// aggregate is forwarded to Set and must remain on Type
|
|
2156
2227
|
addToSetAttr(struct, propName, propValue, propValue !== 'aggregate');
|
|
2157
2228
|
}
|