@sap/cds-compiler 3.0.0 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -9
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +28 -16
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +24 -2
- package/doc/CHANGELOG_DEPRECATED.md +21 -1
- package/lib/api/main.js +92 -40
- package/lib/api/options.js +2 -3
- package/lib/base/keywords.js +64 -1
- package/lib/base/message-registry.js +33 -5
- package/lib/base/messages.js +54 -65
- package/lib/base/model.js +2 -0
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +8 -7
- package/lib/checks/selectItems.js +96 -14
- package/lib/checks/types.js +5 -8
- package/lib/checks/validator.js +1 -2
- package/lib/compiler/assert-consistency.js +65 -13
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +93 -4
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +20 -11
- package/lib/compiler/finalize-parse-cdl.js +5 -9
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +37 -32
- package/lib/compiler/propagator.js +11 -6
- package/lib/compiler/resolve.js +15 -19
- package/lib/compiler/shared.js +54 -18
- package/lib/compiler/tweak-assocs.js +5 -11
- package/lib/compiler/utils.js +15 -6
- package/lib/edm/annotations/genericTranslation.js +12 -2
- package/lib/edm/annotations/preprocessAnnotations.js +18 -15
- package/lib/edm/csn2edm.js +18 -17
- 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 +336 -665
- package/lib/edm/edmUtils.js +86 -45
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -2
- package/lib/gen/languageLexer.js +3 -0
- package/lib/gen/languageParser.js +4332 -4496
- 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 +19 -20
- package/lib/json/to-csn.js +11 -8
- package/lib/language/genericAntlrParser.js +150 -92
- package/lib/language/language.g4 +47 -74
- package/lib/main.d.ts +1 -0
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +56 -29
- package/lib/model/csnUtils.js +29 -14
- package/lib/model/revealInternalProperties.js +6 -4
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +81 -38
- package/lib/render/toCdl.js +57 -32
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +31 -11
- package/lib/render/utils/common.js +3 -4
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +0 -1
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/forHanaNew.js +11 -2
- package/lib/transform/forOdataNew.js +4 -4
- package/lib/transform/localized.js +15 -11
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/utils/file.js +28 -18
- package/lib/utils/moduleResolve.js +0 -1
- package/package.json +3 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/lib/checks/unknownMagic.js +0 -41
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/* eslint max-statements-per-line:off */
|
|
3
3
|
const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
4
|
-
const {
|
|
5
|
-
|
|
4
|
+
const {
|
|
5
|
+
forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
6
|
+
isEdmPropertyRendered, getUtils, cloneCsnNonDict, cloneCsnDictionary,
|
|
7
|
+
isBuiltinType, applyTransformations
|
|
8
|
+
} = require('../model/csnUtils');
|
|
6
9
|
const edmUtils = require('./edmUtils.js');
|
|
10
|
+
const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
|
|
11
|
+
const { inboundQualificationChecks } = require('./edmInboundChecks.js');
|
|
7
12
|
const typesExposure = require('../transform/odata/typesExposure');
|
|
8
13
|
const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
|
|
9
14
|
|
|
10
|
-
const
|
|
11
|
-
intersect,
|
|
12
|
-
validateOptions,
|
|
13
|
-
foreach,
|
|
14
|
-
forAll,
|
|
15
|
-
isAssociationOrComposition,
|
|
16
|
-
isComposition,
|
|
17
|
-
isStructuredArtifact,
|
|
18
|
-
isParameterizedEntity,
|
|
19
|
-
resolveOnConditionAndPrepareConstraints,
|
|
20
|
-
finalizeReferentialConstraints,
|
|
21
|
-
isEntity,
|
|
22
|
-
getSchemaPrefix,
|
|
23
|
-
} = require('./edmUtils.js');
|
|
15
|
+
const NavResAnno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
|
|
24
16
|
|
|
25
17
|
/**
|
|
26
18
|
* edmPreprocessor warms up the model so that it can be converted into an EDM document and
|
|
@@ -34,9 +26,9 @@ const {
|
|
|
34
26
|
*/
|
|
35
27
|
function initializeModel(csn, _options, messageFunctions, requestedServiceNames=undefined)
|
|
36
28
|
{
|
|
37
|
-
const { info, warning, error, message
|
|
29
|
+
const { info, warning, error, message } = messageFunctions;
|
|
38
30
|
|
|
39
|
-
|
|
31
|
+
const csnUtils = getUtils(csn);
|
|
40
32
|
|
|
41
33
|
// proxies are merged into the final model after all proxy elements are collected
|
|
42
34
|
const proxyCache = [];
|
|
@@ -47,7 +39,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
47
39
|
|
|
48
40
|
|
|
49
41
|
// make sure options are complete
|
|
50
|
-
let options = validateOptions(_options);
|
|
42
|
+
let options = edmUtils.validateOptions(_options);
|
|
51
43
|
|
|
52
44
|
const [ serviceRoots,
|
|
53
45
|
serviceRootNames,
|
|
@@ -69,7 +61,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
69
61
|
}
|
|
70
62
|
|
|
71
63
|
// Structural CSN inbound QA checks
|
|
72
|
-
inboundQualificationChecks(
|
|
64
|
+
inboundQualificationChecks(csn, options, messageFunctions,
|
|
65
|
+
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName);
|
|
73
66
|
// not needed at the moment
|
|
74
67
|
// resolveForeignKeyRefs();
|
|
75
68
|
|
|
@@ -124,10 +117,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
124
117
|
(def, defName) => {
|
|
125
118
|
const mySchemaName = whatsMySchemaName(defName);
|
|
126
119
|
mySchemaName && setProp(def, '$mySchemaName', mySchemaName);
|
|
127
|
-
if(isMyServiceRequested(defName))
|
|
120
|
+
if(isMyServiceRequested(defName) && def.kind !== 'aspect')
|
|
128
121
|
reqDefs.definitions[defName] = def;
|
|
129
122
|
},
|
|
130
|
-
linkAssociationTarget
|
|
123
|
+
linkAssociationTarget
|
|
124
|
+
]);
|
|
131
125
|
// initialize requested services
|
|
132
126
|
const skip = { skipArtifact: (_def, defName) => !isMyServiceRequested(defName) };
|
|
133
127
|
forEachDefinition({ definitions: serviceRoots }, initService, skip);
|
|
@@ -142,23 +136,32 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
142
136
|
// Mute V4 elements depending on constraint preparation
|
|
143
137
|
if(options.isV4())
|
|
144
138
|
forEachDefinition(reqDefs, ignoreProperties);
|
|
145
|
-
// calculate constraints based on ignoreProperties and
|
|
139
|
+
// calculate constraints based on ignoreProperties and initConstraints
|
|
146
140
|
forEachDefinition(reqDefs, finalizeConstraints);
|
|
147
141
|
// convert exposed types into cross schema references if required
|
|
148
142
|
// must be run before proxy exposure to avoid potential reference collisions
|
|
149
143
|
convertExposedTypesOfOtherServicesIntoCrossReferences();
|
|
150
|
-
// create association target proxies
|
|
144
|
+
// create association target proxies (v4)
|
|
151
145
|
// Decide if an entity set needs to be constructed or not
|
|
152
|
-
forEachDefinition(reqDefs, [
|
|
146
|
+
forEachDefinition(reqDefs, [
|
|
147
|
+
exposeTargetsAsProxiesOrSchemaRefs,
|
|
148
|
+
determineEntitySet
|
|
149
|
+
]);
|
|
153
150
|
// finalize proxy creation
|
|
154
151
|
mergeProxiesIntoModel();
|
|
155
152
|
|
|
153
|
+
// Calculate NavPropBinding Target paths
|
|
154
|
+
// Rewrite @Capabilities for containment mode
|
|
156
155
|
if(options.isV4())
|
|
157
|
-
forEachDefinition(reqDefs,
|
|
156
|
+
forEachDefinition(reqDefs, [
|
|
157
|
+
initEdmNavPropBindingTargets,
|
|
158
|
+
rewriteContainmentAnnotations,
|
|
159
|
+
annotateOptionalActFuncParams
|
|
160
|
+
]);
|
|
158
161
|
|
|
159
162
|
// Things that can be done in one pass
|
|
160
163
|
// Create edmKeyRefPaths
|
|
161
|
-
// Create NavigationPropertyBindings, requires determineEntitySet
|
|
164
|
+
// Create V4 NavigationPropertyBindings, requires determineEntitySet & initEdmNavPropBindingTargets
|
|
162
165
|
// Map /** doc comments */ to @CoreDescription
|
|
163
166
|
forEachDefinition(reqDefs, [
|
|
164
167
|
initEdmKeyRefPaths,
|
|
@@ -166,7 +169,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
166
169
|
initEdmTypesAndDescription
|
|
167
170
|
]);
|
|
168
171
|
}
|
|
169
|
-
return [
|
|
172
|
+
return [
|
|
173
|
+
serviceRoots,
|
|
174
|
+
schemas,
|
|
175
|
+
reqDefs,
|
|
176
|
+
whatsMyServiceRootName,
|
|
177
|
+
fallBackSchemaName,
|
|
178
|
+
options
|
|
179
|
+
];
|
|
170
180
|
|
|
171
181
|
//////////////////////////////////////////////////////////////////////
|
|
172
182
|
//
|
|
@@ -236,7 +246,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
236
246
|
const rootDef = getRootDef(defName);
|
|
237
247
|
// if this definition has a root def and the root def is not the service/schema name
|
|
238
248
|
// => service C { type D.E }, replace the prefix dots with underscores
|
|
239
|
-
if(rootDef && defName !== rootDef && rootDef !== getSchemaPrefix(defName)) {
|
|
249
|
+
if(rootDef && defName !== rootDef && rootDef !== edmUtils.getSchemaPrefix(defName)) {
|
|
240
250
|
let newDefName = rootDef + '.' + defName.replace(rootDef + '.', '').replace(/\./g, '_');
|
|
241
251
|
// store renamed types in correlation maps for later renaming
|
|
242
252
|
if(def.kind === 'entity')
|
|
@@ -300,7 +310,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
300
310
|
forEachDefinition(csn, (def, defName) => {
|
|
301
311
|
if(def.kind !== 'service') {
|
|
302
312
|
const myServiceRoot = whatsMyServiceRootName(defName);
|
|
303
|
-
const mySchemaPrefix = getSchemaPrefix(defName);
|
|
313
|
+
const mySchemaPrefix = edmUtils.getSchemaPrefix(defName);
|
|
304
314
|
if(myServiceRoot && options.isV4() &&
|
|
305
315
|
/*(options.odataProxies || options.odataXServiceRefs) && options.isStructFormat && */
|
|
306
316
|
defName !== myServiceRoot && myServiceRoot !== mySchemaPrefix) {
|
|
@@ -314,50 +324,48 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
314
324
|
}
|
|
315
325
|
|
|
316
326
|
function attachNameProperty(def, defName) {
|
|
317
|
-
assignProp (def, 'name', defName);
|
|
327
|
+
edmUtils.assignProp (def, 'name', defName);
|
|
318
328
|
// Attach name to bound actions, functions and parameters
|
|
319
329
|
forEachGeneric(def, 'actions', (a, n) => {
|
|
320
|
-
assignProp(a, 'name', n);
|
|
330
|
+
edmUtils.assignProp(a, 'name', n);
|
|
321
331
|
forEachGeneric(a, 'params', (p, n) => {
|
|
322
|
-
assignProp(p, 'name', n);
|
|
332
|
+
edmUtils.assignProp(p, 'name', n);
|
|
323
333
|
});
|
|
324
334
|
});
|
|
325
335
|
// Attach name unbound action parameters
|
|
326
336
|
forEachGeneric(def, 'params', (p,n) => {
|
|
327
|
-
assignProp(p, 'name', n);
|
|
337
|
+
edmUtils.assignProp(p, 'name', n);
|
|
328
338
|
});
|
|
329
339
|
}
|
|
330
340
|
|
|
331
341
|
// initialize the service itself
|
|
332
342
|
function initService(serviceRoot) {
|
|
333
|
-
setSAPSpecificV2AnnotationsToEntityContainer(options, serviceRoot);
|
|
343
|
+
edmAnnoPreproc.setSAPSpecificV2AnnotationsToEntityContainer(options, serviceRoot);
|
|
334
344
|
}
|
|
335
345
|
|
|
336
346
|
// link association target to association and add @odata.contained to compositions in V4
|
|
337
347
|
function linkAssociationTarget(struct) {
|
|
338
348
|
forEachMemberRecursively(struct, (element, name, prop, subpath) => {
|
|
339
|
-
if(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
setProp(element, '_target', target);
|
|
349
|
+
if(element.target && !element._target) {
|
|
350
|
+
let target = csn.definitions[element.target];
|
|
351
|
+
if(target) {
|
|
352
|
+
setProp(element, '_target', target);
|
|
344
353
|
// If target has parameters, xref assoc at target for redirection
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
target.$sources[struct.name + '.' + name] = element;
|
|
354
|
+
if(edmUtils.isParameterizedEntity(target)) {
|
|
355
|
+
if(!target.$sources) {
|
|
356
|
+
setProp(target, '$sources', Object.create(null));
|
|
350
357
|
}
|
|
351
|
-
|
|
352
|
-
else {
|
|
353
|
-
error(null, subpath, { target: element.target }, "Target $(TARGET) can't be found in the model");
|
|
358
|
+
target.$sources[struct.name + '.' + name] = element;
|
|
354
359
|
}
|
|
355
360
|
}
|
|
361
|
+
else {
|
|
362
|
+
error(null, subpath, { target: element.target }, "Target $(TARGET) can't be found in the model");
|
|
363
|
+
}
|
|
356
364
|
}
|
|
357
365
|
// in V4 tag all compositions to be containments
|
|
358
366
|
if(options.odataContainment &&
|
|
359
367
|
options.isV4() &&
|
|
360
|
-
isComposition(element) &&
|
|
368
|
+
edmUtils.isComposition(element) &&
|
|
361
369
|
element['@odata.contained'] === undefined) {
|
|
362
370
|
element['@odata.contained'] = true;
|
|
363
371
|
}
|
|
@@ -367,7 +375,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
367
375
|
// Perform checks and add attributes for "contained" sub-entities:
|
|
368
376
|
// - A container is recognized by having an association/composition annotated with '@odata.contained'.
|
|
369
377
|
// - All targets of such associations ("containees") are marked with a property
|
|
370
|
-
// '
|
|
378
|
+
// '$containerNames: []', having as value an array of container names (i.e. of entities
|
|
371
379
|
// that have a '@odata.contained' association pointing to the containee). Note that this
|
|
372
380
|
// may be multiple entities, possibly including the container itself.
|
|
373
381
|
// - All associations in the containee pointing back to the container are marked with
|
|
@@ -377,35 +385,41 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
377
385
|
// non-containment rendering. If containment rendering is active, the containee has no
|
|
378
386
|
// entity set. Instead try to rewrite the annotation in such a way that it is effective
|
|
379
387
|
// on the containment navigation property.
|
|
388
|
+
// $containeeAssoications stores the containees (children/outbound edges)
|
|
389
|
+
// $containerNames stores the containers (parents/inbound edges)
|
|
390
|
+
|
|
380
391
|
function initContainments(container) {
|
|
381
392
|
if(container.kind === 'entity') {
|
|
393
|
+
if(!container.$containeeAssociations)
|
|
394
|
+
setProp(container, '$containeeAssociations', []);
|
|
382
395
|
forEachMemberRecursively(container, initContainments,
|
|
383
|
-
[], true, { elementsOnly: true });
|
|
396
|
+
[], true, { pathWithoutProp: true, elementsOnly: true });
|
|
384
397
|
}
|
|
385
398
|
|
|
386
|
-
function initContainments(elt,
|
|
387
|
-
if(
|
|
399
|
+
function initContainments(elt, _memberName, _prop, path) {
|
|
400
|
+
if(elt.target && elt['@odata.contained'] && !elt._ignore) {
|
|
401
|
+
// store all containment associations, required to create the containment paths later on
|
|
402
|
+
container.$containeeAssociations.push( { assoc: elt, path });
|
|
388
403
|
// Let the containee know its container
|
|
389
404
|
// (array because the contanee may contained more then once)
|
|
390
405
|
let containee = elt._target;
|
|
391
|
-
if (!containee
|
|
392
|
-
setProp(containee, '
|
|
406
|
+
if (!containee.$containerNames)
|
|
407
|
+
setProp(containee, '$containerNames', []);
|
|
393
408
|
// add container only once per containee
|
|
394
|
-
if (!containee.
|
|
395
|
-
containee.
|
|
409
|
+
if (!containee.$containerNames.includes(container.name))
|
|
410
|
+
containee.$containerNames.push(container.name);
|
|
396
411
|
// Mark associations in the containee pointing to the container (i.e. to this entity)
|
|
397
412
|
forEachMemberRecursively(containee, markToContainer,
|
|
398
|
-
[], true, { elementsOnly: true });
|
|
399
|
-
rewriteContainmentAnnotations(container, containee, eltName);
|
|
413
|
+
[ 'definitions', containee.name ], true, { elementsOnly: true });
|
|
400
414
|
}
|
|
401
|
-
else {
|
|
415
|
+
else if(elt.type && !elt.elements) {
|
|
402
416
|
// try to find elements to drill down further
|
|
403
|
-
while(elt && !
|
|
417
|
+
while(elt && !isBuiltinType(elt.type) && !elt.elements) {
|
|
404
418
|
elt = csn.definitions[elt.type];
|
|
405
419
|
}
|
|
406
420
|
if(elt && elt.elements) {
|
|
407
421
|
forEachMemberRecursively(elt, initContainments,
|
|
408
|
-
|
|
422
|
+
path, true, { pathWithoutProp: true, elementsOnly: true });
|
|
409
423
|
}
|
|
410
424
|
}
|
|
411
425
|
}
|
|
@@ -436,13 +450,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
436
450
|
// Containment processing must take place before because it might be that this
|
|
437
451
|
// artifact with parameters is already contained. In such a case the existing
|
|
438
452
|
// containment chain must be propagated and reused. This requires that the
|
|
439
|
-
// containment data structures must be manually added here
|
|
440
|
-
// must be called.
|
|
453
|
+
// containment data structures must be manually added here.
|
|
441
454
|
// As a param entity is a potential proxy candidate, this split must be performed on
|
|
442
455
|
// all definitions
|
|
443
456
|
function initParameterizedEntityOrView(entityCsn, entityName) {
|
|
444
457
|
|
|
445
|
-
if(!isParameterizedEntity(entityCsn))
|
|
458
|
+
if(!edmUtils.isParameterizedEntity(entityCsn))
|
|
446
459
|
return;
|
|
447
460
|
|
|
448
461
|
// Naming rules for aggregated views with parameters
|
|
@@ -459,7 +472,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
459
472
|
// this code can be extended for aggregated views
|
|
460
473
|
const typeEntityName = entityName + 'Type';
|
|
461
474
|
const typeEntitySetName = entityName + 'Set';
|
|
462
|
-
const parameterToTypeAssocName = 'Set';
|
|
463
475
|
const typeToParameterAssocName = 'Parameters';
|
|
464
476
|
let hasBacklink = true;
|
|
465
477
|
|
|
@@ -505,7 +517,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
505
517
|
<EntitySet Name="ZRHA_TEST_CDSSet" EntityType="ZRHA_TEST_CDS_CDS.ZRHA_TEST_CDSType" sap:creatable="false" sap:updatable="false"
|
|
506
518
|
sap:deletable="false" sap:addressable="false" sap:content-version="1"/>
|
|
507
519
|
*/
|
|
508
|
-
assignProp(entityCsn, '_SetAttributes',
|
|
520
|
+
edmUtils.assignProp(entityCsn, '_SetAttributes',
|
|
509
521
|
{'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.addressable': false });
|
|
510
522
|
|
|
511
523
|
// redirect inbound associations/compositions to the parameter entity
|
|
@@ -515,7 +527,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
515
527
|
entityCsn.$sources[n]._target = parameterCsn;
|
|
516
528
|
entityCsn.$sources[n].target = parameterCsn.name;
|
|
517
529
|
});
|
|
518
|
-
rewriteContainmentAnnotations(parameterCsn, entityCsn, parameterToTypeAssocName);
|
|
519
530
|
}
|
|
520
531
|
|
|
521
532
|
function createParameterEntity(entityCsn, entityName, isProxy) {
|
|
@@ -532,7 +543,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
532
543
|
if(!isProxy)
|
|
533
544
|
setProp(parameterCsn, '$entitySetName', entityName);
|
|
534
545
|
if(entityCsn.$location){
|
|
535
|
-
assignProp(parameterCsn, '$location', entityCsn.$location);
|
|
546
|
+
edmUtils.assignProp(parameterCsn, '$location', entityCsn.$location);
|
|
536
547
|
}
|
|
537
548
|
|
|
538
549
|
/*
|
|
@@ -540,21 +551,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
540
551
|
sap:deletable="false" sap:pageable="false" sap:content-version="1"/>
|
|
541
552
|
*/
|
|
542
553
|
|
|
543
|
-
assignProp(parameterCsn, '_SetAttributes',
|
|
554
|
+
edmUtils.assignProp(parameterCsn, '_SetAttributes',
|
|
544
555
|
{'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.pageable': false });
|
|
545
556
|
|
|
546
557
|
setProp(parameterCsn, '$isParamEntity', true);
|
|
547
558
|
setProp(parameterCsn, '$mySchemaName', entityCsn.$mySchemaName);
|
|
548
559
|
|
|
549
|
-
// propagate containment information, if containment is recursive, use parameterCsn.name as _containerEntity
|
|
550
|
-
if(entityCsn._containerEntity) {
|
|
551
|
-
setProp(parameterCsn, '_containerEntity', []);
|
|
552
|
-
for(const c of entityCsn._containerEntity) {
|
|
553
|
-
parameterCsn._containerEntity.push((c === entityCsn.name) ? parameterCsn.name : c);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
entityCsn._containerEntity = [ parameterCsn ];
|
|
557
|
-
|
|
558
560
|
forEachGeneric(entityCsn, 'params', (p,n) => {
|
|
559
561
|
let elt = cloneCsnNonDict(p, options);
|
|
560
562
|
elt.name = n;
|
|
@@ -569,9 +571,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
569
571
|
and so they are in CAP (all view parameters become primary keys which are not null).
|
|
570
572
|
*/
|
|
571
573
|
if(options.isV2())
|
|
572
|
-
assignAnnotation(elt, '@sap.parameter', 'mandatory');
|
|
574
|
+
edmUtils.assignAnnotation(elt, '@sap.parameter', 'mandatory');
|
|
573
575
|
else
|
|
574
|
-
assignAnnotation(elt, '@Common.FieldControl', { '#': 'Mandatory' });
|
|
576
|
+
edmUtils.assignAnnotation(elt, '@Common.FieldControl', { '#': 'Mandatory' });
|
|
575
577
|
parameterCsn.elements[n] = elt;
|
|
576
578
|
});
|
|
577
579
|
linkAssociationTarget(parameterCsn);
|
|
@@ -589,7 +591,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
589
591
|
setProp(parameterCsn.elements[parameterToTypeAssocName], '$path',
|
|
590
592
|
[ 'definitions', parameterEntityName, 'elements', parameterToTypeAssocName ] );
|
|
591
593
|
}
|
|
592
|
-
|
|
594
|
+
|
|
595
|
+
// initialize containment
|
|
596
|
+
// propagate containment information, if containment is recursive, use parameterCsn.name as $containerNames
|
|
597
|
+
if(entityCsn.$containerNames) {
|
|
598
|
+
if(!parameterCsn.$containerNames)
|
|
599
|
+
setProp(parameterCsn, '$containerNames', []);
|
|
600
|
+
for(const c of entityCsn.$containerNames) {
|
|
601
|
+
parameterCsn.$containerNames.push((c === entityCsn.name) ? parameterCsn.name : c);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
entityCsn.$containerNames = [ parameterCsn ];
|
|
605
|
+
|
|
606
|
+
if(!parameterCsn.$containeeAssociations)
|
|
607
|
+
setProp(parameterCsn, '$containeeAssociations', [ ]);
|
|
608
|
+
parameterCsn.$containeeAssociations.push(
|
|
609
|
+
{ assoc: parameterCsn.elements[parameterToTypeAssocName],
|
|
610
|
+
path: [ parameterToTypeAssocName ]
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// rewrite $path
|
|
593
614
|
setProp(parameterCsn, '$path', [ 'definitions', parameterEntityName ]);
|
|
594
615
|
|
|
595
616
|
// proxies are registered in model separately
|
|
@@ -633,14 +654,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
633
654
|
|
|
634
655
|
// Don't operate on any structured types other than type and entity
|
|
635
656
|
// such as events and aspects
|
|
636
|
-
if(!isStructuredArtifact(def))
|
|
657
|
+
if(!edmUtils.isStructuredArtifact(def))
|
|
637
658
|
return;
|
|
638
659
|
|
|
639
660
|
let keys = Object.create(null);
|
|
640
661
|
let validFrom = [], validKey = [];
|
|
641
662
|
|
|
642
663
|
// Iterate all struct elements
|
|
643
|
-
forEachMemberRecursively(def.items || def, (element, elementName, prop, _path
|
|
664
|
+
forEachMemberRecursively(def.items || def, (element, elementName, prop, _path, construct) => {
|
|
644
665
|
if(prop !== 'elements')
|
|
645
666
|
return;
|
|
646
667
|
|
|
@@ -655,29 +676,31 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
655
676
|
}
|
|
656
677
|
//forward annotations from managed association element to its foreign keys
|
|
657
678
|
const elements = construct.items && construct.items.elements || construct.elements;
|
|
658
|
-
|
|
679
|
+
const fk = elements[element['@odata.foreignKey4']];
|
|
680
|
+
for(const attrName in fk) {
|
|
681
|
+
const attr = fk[attrName];
|
|
659
682
|
if(attrName[0] === '@') {
|
|
660
683
|
element[attrName] = attr;
|
|
661
684
|
}
|
|
662
|
-
}
|
|
685
|
+
}
|
|
663
686
|
// and eventually remove some afterwards:)
|
|
664
687
|
if(options.isV2())
|
|
665
|
-
setSAPSpecificV2AnnotationsToAssociation(element);
|
|
688
|
+
edmAnnoPreproc.setSAPSpecificV2AnnotationsToAssociation(element);
|
|
666
689
|
|
|
667
690
|
// initialize an association
|
|
668
|
-
if(
|
|
691
|
+
if(element.target) {
|
|
669
692
|
// in case this is a forward assoc, store the backlink partners here, _selfReferences.length > 1 => error
|
|
670
|
-
assignProp(element, '_selfReferences', []);
|
|
671
|
-
assignProp(element._target, '$proxies', []);
|
|
693
|
+
edmUtils.assignProp(element, '_selfReferences', []);
|
|
694
|
+
edmUtils.assignProp(element._target, '$proxies', []);
|
|
672
695
|
// $abspath is used as partner path
|
|
673
|
-
assignProp(element, '$abspath', $path2path(element.$path));
|
|
696
|
+
edmUtils.assignProp(element, '$abspath', $path2path(element.$path));
|
|
674
697
|
}
|
|
675
698
|
|
|
676
699
|
// Collect keys
|
|
677
700
|
if (element.key) {
|
|
678
701
|
keys[elementName] = element;
|
|
679
702
|
}
|
|
680
|
-
applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
|
|
703
|
+
edmAnnoPreproc.applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
|
|
681
704
|
}, [], true, { elementsOnly: true });
|
|
682
705
|
|
|
683
706
|
if(!isDeprecatedEnabled(options, '_v1KeysForTemporal')) {
|
|
@@ -685,7 +708,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
685
708
|
if(validKey.length) {
|
|
686
709
|
let altKeys = [{ Key: [] }];
|
|
687
710
|
validKey.forEach(vk => altKeys[0].Key.push( { Name: vk.name, Alias: vk.name } ) );
|
|
688
|
-
assignAnnotation(def, '@Core.AlternateKeys', altKeys);
|
|
711
|
+
edmUtils.assignAnnotation(def, '@Core.AlternateKeys', altKeys);
|
|
689
712
|
}
|
|
690
713
|
}
|
|
691
714
|
else {
|
|
@@ -694,14 +717,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
694
717
|
// @Core.AlternateKeys: [{ Key: [ { Name: 'slID', Alias: 'slID' }, { Name: 'validFrom', Alias: 'validFrom'} ] }]
|
|
695
718
|
if(validKey.length) {
|
|
696
719
|
let altKeys = [{ Key: [] }];
|
|
697
|
-
|
|
720
|
+
Object.entries(([kn, k]) => {
|
|
698
721
|
altKeys[0].Key.push( { Name: kn, Alias: kn } );
|
|
699
722
|
delete k.key;
|
|
700
723
|
});
|
|
701
724
|
validFrom.forEach(e => {
|
|
702
725
|
altKeys[0].Key.push( { Name: e.name, Alias: e.name } );
|
|
703
726
|
});
|
|
704
|
-
assignAnnotation(def, '@Core.AlternateKeys', altKeys);
|
|
727
|
+
edmUtils.assignAnnotation(def, '@Core.AlternateKeys', altKeys);
|
|
705
728
|
keys = Object.create(null);
|
|
706
729
|
validKey.forEach(e => {
|
|
707
730
|
e.key = true;
|
|
@@ -717,27 +740,27 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
717
740
|
}
|
|
718
741
|
|
|
719
742
|
// prepare the structure itself
|
|
720
|
-
if(
|
|
721
|
-
assignProp(def, '_SetAttributes', Object.create(null));
|
|
722
|
-
assignProp(def, '$keys', keys);
|
|
723
|
-
applyAppSpecificLateCsnTransformationOnStructure(options, def, error);
|
|
724
|
-
setSAPSpecificV2AnnotationsToEntitySet(options, def);
|
|
743
|
+
if(def.kind === 'entity') {
|
|
744
|
+
edmUtils.assignProp(def, '_SetAttributes', Object.create(null));
|
|
745
|
+
edmUtils.assignProp(def, '$keys', keys);
|
|
746
|
+
edmAnnoPreproc.applyAppSpecificLateCsnTransformationOnStructure(options, def, error);
|
|
747
|
+
edmAnnoPreproc.setSAPSpecificV2AnnotationsToEntitySet(options, def);
|
|
725
748
|
}
|
|
726
749
|
}
|
|
727
750
|
|
|
728
751
|
// Prepare the associations for the subsequent steps
|
|
729
752
|
function initConstraints(def) {
|
|
730
|
-
if(!isStructuredArtifact(def))
|
|
753
|
+
if(!edmUtils.isStructuredArtifact(def))
|
|
731
754
|
return;
|
|
732
755
|
|
|
733
756
|
forEachMemberRecursively(def.items || def, initConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
734
757
|
}
|
|
735
758
|
function initConstraintsOnAssoc(element) {
|
|
736
|
-
if (
|
|
759
|
+
if (element.target && !element._constraints) {
|
|
737
760
|
// setup the constraints object
|
|
738
761
|
setProp(element, '_constraints', { constraints: Object.create(null), selfs: [], _origins: [], termCount: 0 });
|
|
739
762
|
// and crack the ON condition
|
|
740
|
-
resolveOnConditionAndPrepareConstraints(csn, element, messageFunctions);
|
|
763
|
+
edmUtils.resolveOnConditionAndPrepareConstraints(csn, element, messageFunctions);
|
|
741
764
|
}
|
|
742
765
|
}
|
|
743
766
|
|
|
@@ -754,7 +777,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
754
777
|
4) All of this can be revoked with options.renderForeignKeys.
|
|
755
778
|
*/
|
|
756
779
|
function ignoreProperties(struct) {
|
|
757
|
-
if(!isStructuredArtifact(struct))
|
|
780
|
+
if(!edmUtils.isStructuredArtifact(struct))
|
|
758
781
|
return;
|
|
759
782
|
|
|
760
783
|
forEachMemberRecursively(struct.items || struct, (element) => {
|
|
@@ -778,7 +801,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
778
801
|
*/
|
|
779
802
|
if((!isContainerAssoc && !isEdmPropertyRendered(element, options)) ||
|
|
780
803
|
(isContainerAssoc && !options.renderForeignKeys))
|
|
781
|
-
assignAnnotation(element, '@cds.api.ignore', true);
|
|
804
|
+
edmUtils.assignAnnotation(element, '@cds.api.ignore', true);
|
|
782
805
|
// Only in containment:
|
|
783
806
|
// If this element is a foreign key and if it is rendered, remove it from the key ref vector
|
|
784
807
|
else if(options.odataContainment && isContainerAssoc && options.renderForeignKeys) {
|
|
@@ -790,7 +813,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
790
813
|
// Ignore this (foreign key) elment if renderForeignKeys is false
|
|
791
814
|
if(options.odataContainment && element['@odata.containment.ignore']) {
|
|
792
815
|
if(!options.renderForeignKeys)
|
|
793
|
-
assignAnnotation(element, '@cds.api.ignore', true);
|
|
816
|
+
edmUtils.assignAnnotation(element, '@cds.api.ignore', true);
|
|
794
817
|
else
|
|
795
818
|
// If foreign keys shall be rendered, remove it from key ref vector
|
|
796
819
|
delete struct.$keys[element.name];
|
|
@@ -800,7 +823,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
800
823
|
else if(element['@odata.containment.ignore'] && options.odataContainment && !options.renderForeignKeys) {
|
|
801
824
|
// if this is an explicitly containment ignore tagged association,
|
|
802
825
|
// ignore it if option odataContainment is true and no foreign keys should be rendered
|
|
803
|
-
assignAnnotation(element, '@odata.navigable', false);
|
|
826
|
+
edmUtils.assignAnnotation(element, '@odata.navigable', false);
|
|
804
827
|
}
|
|
805
828
|
}, [], true, { elementsOnly: true });
|
|
806
829
|
}
|
|
@@ -811,14 +834,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
811
834
|
in edmUtils
|
|
812
835
|
*/
|
|
813
836
|
function finalizeConstraints(def) {
|
|
814
|
-
if(!isStructuredArtifact(def))
|
|
837
|
+
if(!edmUtils.isStructuredArtifact(def))
|
|
815
838
|
return;
|
|
816
839
|
|
|
817
840
|
forEachMemberRecursively(def.items || def, finalizeConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
818
841
|
}
|
|
819
842
|
function finalizeConstraintsOnAssoc(element) {
|
|
820
|
-
if (
|
|
821
|
-
finalizeReferentialConstraints(csn, element, options, info);
|
|
843
|
+
if (element.target && !element._ignore && element._constraints) {
|
|
844
|
+
edmUtils.finalizeReferentialConstraints(csn, element, options, info);
|
|
822
845
|
|
|
823
846
|
if(element._constraints._partnerCsn && element.cardinality && element.cardinality.max) {
|
|
824
847
|
// if this is a partnership and this assoc has a set target cardinality, assign it as source cardinality to the partner
|
|
@@ -907,7 +930,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
907
930
|
// if this artifact is a service member check its associations
|
|
908
931
|
if(globalSchemaPrefix) {
|
|
909
932
|
forEachGeneric(struct.items || struct, 'elements', element => {
|
|
910
|
-
if(!
|
|
933
|
+
if(!element.target || element['@odata.navigable'] === false)
|
|
911
934
|
return;
|
|
912
935
|
/*
|
|
913
936
|
* Consider everything @cds.autoexpose: falsy to be a proxy candidate for now
|
|
@@ -915,7 +938,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
915
938
|
/*
|
|
916
939
|
if(element._target['@cds.autoexpose'] === false) {
|
|
917
940
|
// :TODO: Also _ignore foreign keys to association?
|
|
918
|
-
foreach(struct.elements,
|
|
941
|
+
edmUtils.foreach(struct.elements,
|
|
919
942
|
e =>
|
|
920
943
|
e['@odata.foreignKey4'] === element.name,
|
|
921
944
|
e => e._ignore = true);
|
|
@@ -943,23 +966,27 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
943
966
|
|
|
944
967
|
const targetSchemaName = element._target.$mySchemaName;
|
|
945
968
|
if(isProxyRequired(element)) {
|
|
946
|
-
if(options.isV4() &&
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
element.
|
|
950
|
-
(element.on && element._constraints.selfs.length === 1 && element._constraints.termCount === 1)
|
|
951
|
-
) {
|
|
969
|
+
if(options.isV4() && (options.odataProxies || options.odataXServiceRefs)) {
|
|
970
|
+
// must be a managed association with keys OR an unambiguous backlink to become a proxy
|
|
971
|
+
let assocOK = element.keys ||
|
|
972
|
+
(element.on && element._constraints.selfs.length === 1 && element._constraints.termCount === 1);
|
|
952
973
|
// reuse proxy if available
|
|
953
974
|
let proxy = getProxyForTargetOf(element);
|
|
954
975
|
if(!proxy) {
|
|
955
976
|
if(targetSchemaName && options.odataXServiceRefs) {
|
|
956
977
|
proxy = createSchemaRefFor(targetSchemaName);
|
|
957
978
|
}
|
|
958
|
-
|
|
979
|
+
// create a proxy for a 'good' association only
|
|
980
|
+
else if(options.odataProxies && assocOK) {
|
|
959
981
|
proxy = createProxyFor(element, targetSchemaName);
|
|
960
982
|
}
|
|
961
983
|
proxy = registerProxy(proxy, element);
|
|
962
|
-
}
|
|
984
|
+
} else if(!assocOK) {
|
|
985
|
+
// if there is already a proxy (generated by a 'good' association)
|
|
986
|
+
// and this association is not a good one, don't expose this association.
|
|
987
|
+
muteNavProp(element, 'oncond');
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
963
990
|
if(proxy) {
|
|
964
991
|
// if a proxy was either already created or could be created and
|
|
965
992
|
// if it's a 'real' proxy, link the _target to it and remove constraints
|
|
@@ -977,24 +1004,22 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
977
1004
|
}
|
|
978
1005
|
}
|
|
979
1006
|
else {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
noNavPropMsg(element);
|
|
1007
|
+
muteNavProp(element, assocOK ? 'std' : 'oncond');
|
|
1008
|
+
return;
|
|
983
1009
|
}
|
|
984
1010
|
}
|
|
985
|
-
// ok schema names are different, now check if external wants to link back into its service schema
|
|
986
1011
|
else {
|
|
987
|
-
|
|
988
|
-
noNavPropMsg(element);
|
|
1012
|
+
muteNavProp(element);
|
|
989
1013
|
return;
|
|
990
1014
|
}
|
|
991
1015
|
}
|
|
992
1016
|
});
|
|
993
1017
|
}
|
|
994
1018
|
|
|
995
|
-
function
|
|
1019
|
+
function muteNavProp(elt, msg='std') {
|
|
1020
|
+
edmUtils.assignAnnotation(elt, '@odata.navigable', false);
|
|
996
1021
|
warning('odata-navigation', ['definitions', struct.name, 'elements', elt.name],
|
|
997
|
-
{ target: elt._target.name, service: globalSchemaPrefix });
|
|
1022
|
+
{ target: elt._target.name, service: globalSchemaPrefix, '#': msg });
|
|
998
1023
|
}
|
|
999
1024
|
|
|
1000
1025
|
function createSchemaRefFor(targetSchemaName) {
|
|
@@ -1010,10 +1035,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1010
1035
|
// If target is outside any service expose it in service of source entity
|
|
1011
1036
|
// The proxySchemaName is not prepended with the service schema name to allow to share the proxy
|
|
1012
1037
|
// if it is required in multiple services. The service schema name is prepended upon registration
|
|
1013
|
-
const proxySchemaName = targetSchemaName || getSchemaPrefix(assoc._target.name);
|
|
1038
|
+
const proxySchemaName = targetSchemaName || edmUtils.getSchemaPrefix(assoc._target.name);
|
|
1014
1039
|
|
|
1015
1040
|
// if the target is a parameter entity, it's easy just create the parameter stub
|
|
1016
|
-
const isParamProxy = isParameterizedEntity(assoc._target);
|
|
1041
|
+
const isParamProxy = edmUtils.isParameterizedEntity(assoc._target);
|
|
1017
1042
|
|
|
1018
1043
|
// 1) construct the proxy definition
|
|
1019
1044
|
// proxyDefinitionName: strip the serviceName and replace '.' with '_'
|
|
@@ -1036,7 +1061,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1036
1061
|
setProp(proxy, '$exposedTypes', Object.create(null));
|
|
1037
1062
|
// copy all annotations of the target to the proxy
|
|
1038
1063
|
Object.entries(assoc._target).forEach(([k, v]) => {
|
|
1039
|
-
if(k[0] === '@')
|
|
1064
|
+
if(k[0] === '@' && k !== '@open')
|
|
1040
1065
|
proxy[k] = v;
|
|
1041
1066
|
});
|
|
1042
1067
|
|
|
@@ -1063,7 +1088,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1063
1088
|
// copy over the primary keys of the target and trigger the type exposure
|
|
1064
1089
|
// if the element already exists we assume it was fully exposed
|
|
1065
1090
|
function populateProxyElements(assoc, proxy, elements) {
|
|
1066
|
-
|
|
1091
|
+
Object.values(elements).forEach(e => {
|
|
1067
1092
|
if (isEdmPropertyRendered(e, options)) {
|
|
1068
1093
|
let newElt = proxy.elements[e.name];
|
|
1069
1094
|
if(!newElt) {
|
|
@@ -1117,7 +1142,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1117
1142
|
// to notNull until the first named type is exposed.
|
|
1118
1143
|
function exposeStructTypeForProxyOf(proxy, node, artificialName,
|
|
1119
1144
|
typeSchemaName=fallBackSchemaName,
|
|
1120
|
-
isKey, forceToNotNull) {
|
|
1145
|
+
isKey = false, forceToNotNull = false) {
|
|
1121
1146
|
|
|
1122
1147
|
if(node.type && isBuiltinType(node.type))
|
|
1123
1148
|
return;
|
|
@@ -1141,7 +1166,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1141
1166
|
let typeId = artificialName; // the artificialName has no namespace, it's the element
|
|
1142
1167
|
if(node.type) {
|
|
1143
1168
|
// same as for proxies, use schema or namespace, 'root' is last resort
|
|
1144
|
-
typeSchemaName = typeDef.$mySchemaName || getSchemaPrefix(node.type);
|
|
1169
|
+
typeSchemaName = typeDef.$mySchemaName || edmUtils.getSchemaPrefix(node.type);
|
|
1145
1170
|
typeId = node.type.replace(typeSchemaName + '.', '').replace(/\./g, '_');
|
|
1146
1171
|
// strip the service root of that type (if any)
|
|
1147
1172
|
const myServiceRootName = whatsMyServiceRootName(typeSchemaName);
|
|
@@ -1149,7 +1174,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1149
1174
|
typeSchemaName = typeSchemaName.replace(myServiceRootName + '.', '');
|
|
1150
1175
|
}
|
|
1151
1176
|
|
|
1152
|
-
if(isStructuredArtifact(typeDef)) {
|
|
1177
|
+
if(edmUtils.isStructuredArtifact(typeDef)) {
|
|
1153
1178
|
// pull forceNotNull to false for named types and non-key nodes
|
|
1154
1179
|
// only toplevel nodes (elements) can be key
|
|
1155
1180
|
forceToNotNull = !!(forceToNotNull && isKey && node.elements && !node.type);
|
|
@@ -1208,6 +1233,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1208
1233
|
};
|
|
1209
1234
|
setProp(type, '$mySchemaName', typeSchemaName);
|
|
1210
1235
|
setProp(type, '$exposedBy', 'proxyExposure');
|
|
1236
|
+
if(typeDef['@open'] !== undefined)
|
|
1237
|
+
type['@open'] = typeDef['@open'];
|
|
1211
1238
|
|
|
1212
1239
|
typeDef.elements && Object.entries(typeDef.elements).forEach( ([elemName, elem]) => {
|
|
1213
1240
|
if(!elem.target) {
|
|
@@ -1284,7 +1311,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1284
1311
|
if(proxy === undefined) {
|
|
1285
1312
|
proxy = e._target;
|
|
1286
1313
|
// no proxy: no navigation
|
|
1287
|
-
assignAnnotation(newElt, '@odata.navigable', false);
|
|
1314
|
+
edmUtils.assignAnnotation(newElt, '@odata.navigable', false);
|
|
1288
1315
|
}
|
|
1289
1316
|
// either the proxy has exposed the type or
|
|
1290
1317
|
// the assoc doesn't need to be exposed, so don't
|
|
@@ -1350,7 +1377,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1350
1377
|
const fqProxyName = proxy.$globalSchemaPrefix + '.' + proxy.name;
|
|
1351
1378
|
|
|
1352
1379
|
if(!element._target.$cachedProxy)
|
|
1353
|
-
assignProp(element._target, '$cachedProxy', Object.create(null));
|
|
1380
|
+
edmUtils.assignProp(element._target, '$cachedProxy', Object.create(null));
|
|
1354
1381
|
if(getProxyForTargetOf(element)) {
|
|
1355
1382
|
info(null, ['definitions', struct.name, 'elements', element.name],
|
|
1356
1383
|
{ name: fqProxyName }, 'Proxy EDM entity type $(NAME) has already been registered');
|
|
@@ -1366,10 +1393,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1366
1393
|
|
|
1367
1394
|
function mergeProxiesIntoModel() {
|
|
1368
1395
|
proxyCache.forEach(proxy => {
|
|
1396
|
+
|
|
1369
1397
|
const fqProxyName = proxy.$globalSchemaPrefix + '.' + proxy.name;
|
|
1370
1398
|
const fqSchemaName = proxy.$globalSchemaPrefix + '.' + proxy.$mySchemaName;
|
|
1371
1399
|
|
|
1372
1400
|
if(proxy.kind === 'entity') {
|
|
1401
|
+
finalizeProxyContainments(proxy);
|
|
1373
1402
|
// collect all schemas even for newly exposed types
|
|
1374
1403
|
// (that may reside in another subcontext schema), but only once
|
|
1375
1404
|
const schemaSet = new Set();
|
|
@@ -1379,7 +1408,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1379
1408
|
// don't forget to prepend the global namespace prefix
|
|
1380
1409
|
// schemas are ordered in csn2edm.js for each service
|
|
1381
1410
|
Object.keys(proxy.$exposedTypes).forEach(t =>
|
|
1382
|
-
schemaSet.add(proxy.$globalSchemaPrefix + '.' + getSchemaPrefix(t)));
|
|
1411
|
+
schemaSet.add(proxy.$globalSchemaPrefix + '.' + edmUtils.getSchemaPrefix(t)));
|
|
1383
1412
|
schemaSet.forEach(schemaName => {
|
|
1384
1413
|
if(!schemas[schemaName]) {
|
|
1385
1414
|
schemas[schemaName] = { kind: 'schema', name: schemaName };
|
|
@@ -1400,6 +1429,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1400
1429
|
setProp(v, '$path', ['definitions', fqtn]);
|
|
1401
1430
|
}
|
|
1402
1431
|
});
|
|
1432
|
+
|
|
1403
1433
|
// default location is not always correct in case proxy has been created by a nested assoc
|
|
1404
1434
|
// as foreign key targeting another proxy association
|
|
1405
1435
|
let loc = ['definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name];
|
|
@@ -1427,6 +1457,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1427
1457
|
});
|
|
1428
1458
|
// sort the global schemaNames array
|
|
1429
1459
|
schemaNames.sort((a,b) => b.length-a.length);
|
|
1460
|
+
|
|
1461
|
+
function finalizeProxyContainments(proxy) {
|
|
1462
|
+
// initialise containments after all exposed types are collected
|
|
1463
|
+
// AND remove unfullfillable NavRestrictions
|
|
1464
|
+
initContainments(proxy);
|
|
1465
|
+
const assocPaths = proxy.$containeeAssociations.map(entry => entry.path.join('.'));
|
|
1466
|
+
const newNpr = [];
|
|
1467
|
+
const npr = proxy[NavResAnno];
|
|
1468
|
+
npr && npr.forEach(np => {
|
|
1469
|
+
const npath = np.NavigationProperty && np.NavigationProperty['='];
|
|
1470
|
+
if(npath && assocPaths.includes(npath))
|
|
1471
|
+
newNpr.push(np);
|
|
1472
|
+
});
|
|
1473
|
+
if(newNpr.length) {
|
|
1474
|
+
proxy[NavResAnno] = newNpr;
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
delete proxy[NavResAnno]
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1430
1480
|
}
|
|
1431
1481
|
|
|
1432
1482
|
/*
|
|
@@ -1446,7 +1496,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1446
1496
|
if(def.$keys) {
|
|
1447
1497
|
setProp(def, '$edmKeyPaths', []);
|
|
1448
1498
|
// for all key elements that shouldn't be ignored produce the paths
|
|
1449
|
-
foreach(def.$keys, k => !(k._isToContainer && k._selfReferences.length), (k, kn) => {
|
|
1499
|
+
edmUtils.foreach(def.$keys, k => !(k._isToContainer && k._selfReferences.length), (k, kn) => {
|
|
1450
1500
|
if(isEdmPropertyRendered(k, options) &&
|
|
1451
1501
|
!(options.isV2() && k['@Core.MediaType'])) {
|
|
1452
1502
|
if(options.isV4() && options.isStructFormat) {
|
|
@@ -1551,7 +1601,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1551
1601
|
|
|
1552
1602
|
// check for legal scalar types, proxy exposed structured types are not resolvable in CSN
|
|
1553
1603
|
// V2 allows any Edm.PrimitiveType (even Double and Binary), V4 is more specific:
|
|
1554
|
-
if(options.isV4() && type && !
|
|
1604
|
+
if(options.isV4() && type && !type.target && isBuiltinType(type.type)) {
|
|
1555
1605
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1556
1606
|
const legalEdmTypes = {
|
|
1557
1607
|
'Edm.Boolean':1, 'Edm.Byte':1, 'Edm.Date':1, 'Edm.DateTimeOffset':1, 'Edm.Decimal':1, 'Edm.Duration':1,
|
|
@@ -1604,7 +1654,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1604
1654
|
if(isEdmPropertyRendered(elt, options)) {
|
|
1605
1655
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1606
1656
|
// unroll derived type chains for assocs
|
|
1607
|
-
if(
|
|
1657
|
+
if(elt.target && !elt.$visited) {
|
|
1608
1658
|
if(!elt._target.$edmTgtPaths)
|
|
1609
1659
|
setProp(elt._target, '$edmTgtPaths', []);
|
|
1610
1660
|
// drill into target only if
|
|
@@ -1616,10 +1666,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1616
1666
|
!elt._isToContainer &&
|
|
1617
1667
|
curDef !== elt._target) {
|
|
1618
1668
|
// follow elements in the target but avoid cycles
|
|
1619
|
-
setProp(elt, '$
|
|
1669
|
+
setProp(elt, '$visited', true);
|
|
1620
1670
|
elt._target.$edmTgtPaths.push(newPrefix);
|
|
1621
1671
|
Object.values(elt._target.elements).forEach(e => produceTargetPath(newPrefix, e, elt._target));
|
|
1622
|
-
delete elt.$
|
|
1672
|
+
delete elt.$visited;
|
|
1623
1673
|
}
|
|
1624
1674
|
}
|
|
1625
1675
|
else {
|
|
@@ -1649,7 +1699,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1649
1699
|
if(isEdmPropertyRendered(elt, options)) {
|
|
1650
1700
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1651
1701
|
// unroll derived type chains for assocs
|
|
1652
|
-
if(
|
|
1702
|
+
if(elt.target && !elt.$visited) {
|
|
1653
1703
|
// drill into target only if
|
|
1654
1704
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1655
1705
|
// 2) current definition and target are the same (cycle)
|
|
@@ -1659,9 +1709,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1659
1709
|
!elt._isToContainer &&
|
|
1660
1710
|
curDef !== elt._target) {
|
|
1661
1711
|
// follow elements in the target but avoid cycles
|
|
1662
|
-
setProp(elt, '$
|
|
1712
|
+
setProp(elt, '$visited', true);
|
|
1663
1713
|
Object.values(elt._target.elements).forEach(e => npbs = npbs.concat(produceNavigationPath(e, elt._target)));
|
|
1664
|
-
delete elt.$
|
|
1714
|
+
delete elt.$visited;
|
|
1665
1715
|
}
|
|
1666
1716
|
else if(!(options.odataContainment && options.isV4() && elt['@odata.contained'])) {
|
|
1667
1717
|
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
@@ -1713,7 +1763,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1713
1763
|
// 1) must not be a proxy and not a containee in V4
|
|
1714
1764
|
// No annos are rendered for non-existing EntitySet targets.
|
|
1715
1765
|
if(def.$hasEntitySet === undefined) {
|
|
1716
|
-
const hasEntitySet =
|
|
1766
|
+
const hasEntitySet = def.kind === 'entity' && !(options.isV4() && edmUtils.isContainee(def)) && !def.$proxy;
|
|
1717
1767
|
setProp(def, '$hasEntitySet', hasEntitySet);
|
|
1718
1768
|
}
|
|
1719
1769
|
}
|
|
@@ -1722,7 +1772,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1722
1772
|
// 1. let all doc props become @Core.Descriptions
|
|
1723
1773
|
// 2. mark a member that will become a collection
|
|
1724
1774
|
// 3. assign the edm primitive type to elements, to be used in the rendering later
|
|
1725
|
-
assignAnnotation(def, '@Core.Description', def.doc);
|
|
1775
|
+
edmUtils.assignAnnotation(def, '@Core.Description', def.doc);
|
|
1726
1776
|
markCollection(def);
|
|
1727
1777
|
mapCdsToEdmProp(def);
|
|
1728
1778
|
if (def.returns) {
|
|
@@ -1730,7 +1780,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1730
1780
|
mapCdsToEdmProp(def.returns);
|
|
1731
1781
|
}
|
|
1732
1782
|
forEachMemberRecursively(def,member => {
|
|
1733
|
-
assignAnnotation(member, '@Core.Description', member.doc);
|
|
1783
|
+
edmUtils.assignAnnotation(member, '@Core.Description', member.doc);
|
|
1734
1784
|
markCollection(member);
|
|
1735
1785
|
mapCdsToEdmProp(member);
|
|
1736
1786
|
ComputedDefaultValue(member);
|
|
@@ -1743,98 +1793,160 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1743
1793
|
function markCollection(obj) {
|
|
1744
1794
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
1745
1795
|
if (items) {
|
|
1746
|
-
assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : true);
|
|
1747
|
-
assignProp(obj, '_isCollection', true);
|
|
1796
|
+
edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : true);
|
|
1797
|
+
edmUtils.assignProp(obj, '_isCollection', true);
|
|
1748
1798
|
}
|
|
1749
1799
|
}
|
|
1750
1800
|
}
|
|
1751
1801
|
|
|
1802
|
+
// If containment in V4 is active, annotations that would be assigned to the containees
|
|
1803
|
+
// entity set are not renderable anymore. In such a case try to reassign the annotations to
|
|
1804
|
+
// the containment navigation property.
|
|
1805
|
+
// Today only Capabilities.*Restrictions are known to be remapped as there exists a CDS
|
|
1806
|
+
// short cut annotation @readonly that gets expanded and can be safely remapped.
|
|
1807
|
+
function rewriteContainmentAnnotations(rootContainer) {
|
|
1752
1808
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
//
|
|
1809
|
+
// meaningless for non-entities and proxies
|
|
1810
|
+
if(rootContainer.kind !== 'entity' || rootContainer.$proxy)
|
|
1811
|
+
return;
|
|
1757
1812
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1813
|
+
const isRecursiveContainment =
|
|
1814
|
+
!!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
1815
|
+
rootContainer.$containerNames.length === 1 &&
|
|
1816
|
+
rootContainer.$containeeAssociations.some(entry => rootContainer.$containerNames.includes(entry.assoc.target)));
|
|
1817
|
+
|
|
1818
|
+
// Root nodes are not contained
|
|
1819
|
+
const isRootNode =
|
|
1820
|
+
!!(!rootContainer.$containerNames ||
|
|
1821
|
+
rootContainer.$containerNames && rootContainer.$containerNames.length === 0);
|
|
1822
|
+
|
|
1823
|
+
if(!isRecursiveContainment && !isRootNode)
|
|
1824
|
+
return;
|
|
1825
|
+
|
|
1826
|
+
const rootRestrictions = [];
|
|
1827
|
+
addContainmentAnnotationsRecursively([], rootContainer);
|
|
1828
|
+
if(rootRestrictions.length)
|
|
1829
|
+
rootContainer[NavResAnno] = rootRestrictions;
|
|
1830
|
+
|
|
1831
|
+
function addContainmentAnnotationsRecursively(prefix, container) {
|
|
1832
|
+
if(container.$containeeAssociations) {
|
|
1833
|
+
// copy or create container restrictions, don't modify original
|
|
1834
|
+
const localRestrictions = container[NavResAnno] ?
|
|
1835
|
+
cloneCsnDictionary(container[NavResAnno], options) : []
|
|
1836
|
+
|
|
1837
|
+
// prefix the existing navigation property restritictions on the container
|
|
1838
|
+
if(prefix.length) {
|
|
1839
|
+
localRestrictions.forEach(npe => {
|
|
1840
|
+
if(npe.NavigationProperty &&
|
|
1841
|
+
npe.NavigationProperty['='] &&
|
|
1842
|
+
typeof npe.NavigationProperty['='] === 'string') {
|
|
1843
|
+
applyTransformations({ definitions: { npe }}, {
|
|
1844
|
+
"=": (parent, prop, value) => {
|
|
1845
|
+
parent[prop] = prefix.concat(value).join('.');
|
|
1846
|
+
}
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1766
1849
|
});
|
|
1767
1850
|
}
|
|
1768
|
-
}, currPath, true, { elementsOnly: true });
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
1851
|
|
|
1852
|
+
setProp(container, '$visited', true);
|
|
1853
|
+
// collect capabilities from containees
|
|
1854
|
+
container.$containeeAssociations.forEach(entry => {
|
|
1855
|
+
const { assoc, path } = entry;
|
|
1856
|
+
const containee = assoc._target;
|
|
1772
1857
|
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1858
|
+
if(isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
1859
|
+
const localAssocPath = path.join('.');
|
|
1860
|
+
let navPropEntry;
|
|
1861
|
+
const hasEntry = !!(navPropEntry = localRestrictions.find(p =>
|
|
1862
|
+
p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.')));
|
|
1777
1863
|
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
forEachMemberRecursively(def,
|
|
1782
|
-
(member, _memberName, _prop, path) => {
|
|
1783
|
-
setProp(member, '$path', path);
|
|
1784
|
-
}, [ 'definitions', defName ]);
|
|
1785
|
-
}
|
|
1864
|
+
if(!hasEntry) {
|
|
1865
|
+
navPropEntry = { NavigationProperty: { '=': prefix.concat(localAssocPath).join('.') } };
|
|
1866
|
+
}
|
|
1786
1867
|
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1868
|
+
const props = Object.entries(containee);
|
|
1869
|
+
let newEntry = false;
|
|
1870
|
+
['@Capabilities.DeleteRestrictions',
|
|
1871
|
+
'@Capabilities.InsertRestrictions',
|
|
1872
|
+
'@Capabilities.UpdateRestrictions',
|
|
1873
|
+
'@Capabilities.ReadRestrictions'].forEach(c => {
|
|
1874
|
+
if(edmUtils.mergeIntoNavPropEntry(c, navPropEntry, prefix.concat(path), props))
|
|
1875
|
+
newEntry = true;
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
if(newEntry && !hasEntry) {
|
|
1879
|
+
localRestrictions.push(navPropEntry);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if(!containee.$visited) {
|
|
1883
|
+
addContainmentAnnotationsRecursively(prefix.concat(path), containee);
|
|
1884
|
+
}
|
|
1800
1885
|
}
|
|
1886
|
+
});
|
|
1801
1887
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
message('chained-array-of', path);
|
|
1805
|
-
}
|
|
1888
|
+
rootRestrictions.unshift(...localRestrictions);
|
|
1889
|
+
delete container.$visited;
|
|
1806
1890
|
}
|
|
1807
1891
|
}
|
|
1892
|
+
}
|
|
1808
1893
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1894
|
+
/*
|
|
1895
|
+
V4 Only:
|
|
1896
|
+
An action/function parameter is optional if
|
|
1897
|
+
1) it is explicitly annotated to be optional
|
|
1898
|
+
2) it has a default value (including null), regardless of it's nullability
|
|
1899
|
+
3) it has NO default value but is nullable (the implicit default value is null)
|
|
1900
|
+
|
|
1901
|
+
If a mandatory parameter (not null no default value) appears after an optional
|
|
1902
|
+
parameter, a warning is raised, Core.OptionalParameter requires that all optional
|
|
1903
|
+
parameters appear rightmost.
|
|
1904
|
+
*/
|
|
1905
|
+
function annotateOptionalActFuncParams(def, defName) {
|
|
1906
|
+
if(!isBetaEnabled(options, 'optionalActionFunctionParameters'))
|
|
1907
|
+
return;
|
|
1908
|
+
// return if there is nothing to do
|
|
1909
|
+
const loc = [ 'definitions', defName ]
|
|
1910
|
+
if(def.kind === 'function' || def.kind === 'action')
|
|
1911
|
+
iterateParams(def, loc.concat('params'));
|
|
1912
|
+
if(def.actions) {
|
|
1913
|
+
for(const an in def.actions) {
|
|
1914
|
+
const a = def.actions[an];
|
|
1915
|
+
iterateParams(a, loc.concat(['actions', an, 'params' ]));
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1817
1918
|
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1919
|
+
function iterateParams(def, loc) {
|
|
1920
|
+
let lastOptPos = 0;
|
|
1921
|
+
let i = 0;
|
|
1922
|
+
for(const pn in def.params) {
|
|
1923
|
+
const p = def.params[pn];
|
|
1924
|
+
// user assigned annotation, don't touch it
|
|
1925
|
+
if(Object.keys(p).some(a => a.startsWith('@Core.OptionalParameter') && p[a] !== null)) {
|
|
1926
|
+
lastOptPos = i;
|
|
1825
1927
|
}
|
|
1826
|
-
|
|
1928
|
+
// default value automatically makes param optional
|
|
1929
|
+
else if(p.default) {
|
|
1930
|
+
if(p.default.val)
|
|
1931
|
+
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.DefaultValue', p.default.val);
|
|
1932
|
+
else
|
|
1933
|
+
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
1934
|
+
lastOptPos = i;
|
|
1935
|
+
}
|
|
1936
|
+
// if no default is available, nullable makes param optional
|
|
1937
|
+
else if(!p.notNull) {
|
|
1938
|
+
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
1939
|
+
lastOptPos = i;
|
|
1940
|
+
}
|
|
1941
|
+
// check if mandatory param follows optional param
|
|
1942
|
+
else if(lastOptPos < i) {
|
|
1943
|
+
warning('odata-parameter-order', loc.concat(pn));
|
|
1944
|
+
}
|
|
1945
|
+
i++;
|
|
1946
|
+
}
|
|
1827
1947
|
}
|
|
1828
1948
|
}
|
|
1829
1949
|
|
|
1830
|
-
//
|
|
1831
|
-
// Checks Secition ends here
|
|
1832
|
-
//
|
|
1833
|
-
//////////////////////////////////////////////////////////////////////
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
1950
|
//////////////////////////////////////////////////////////////////////
|
|
1839
1951
|
//
|
|
1840
1952
|
// Helper section starts here
|
|
@@ -1879,96 +1991,18 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1879
1991
|
}
|
|
1880
1992
|
}
|
|
1881
1993
|
|
|
1882
|
-
|
|
1883
|
-
// If containment in V4 is active, annotations that would be assigned to the containees
|
|
1884
|
-
// entity set are not renderable anymore. In such a case try to reassign the annotations to
|
|
1885
|
-
// the containment navigation property.
|
|
1886
|
-
// Today only Capabilities.*Restrictions are known to be remapped as there exists a CDS
|
|
1887
|
-
// short cut annotation @readonly that gets expanded and can be safely remapped.
|
|
1888
|
-
function rewriteContainmentAnnotations(container, containee, assocName) {
|
|
1889
|
-
// rectify Restrictions to NavigationRestrictions
|
|
1890
|
-
if(options.isV4()) {
|
|
1891
|
-
let navPropEntry;
|
|
1892
|
-
let hasEntry = false;
|
|
1893
|
-
let newEntry = false;
|
|
1894
|
-
const anno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
|
|
1895
|
-
let resProps = container[anno];
|
|
1896
|
-
// merge into existing anno, if available
|
|
1897
|
-
if(resProps) {
|
|
1898
|
-
navPropEntry = resProps.find(p => p.NavigationProperty && p.NavigationProperty['='] === assocName);
|
|
1899
|
-
hasEntry = !!navPropEntry;
|
|
1900
|
-
}
|
|
1901
|
-
if(!navPropEntry) {
|
|
1902
|
-
navPropEntry = { NavigationProperty: { '=': assocName } };
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
const props = Object.entries(containee);
|
|
1906
|
-
|
|
1907
|
-
const merge = (prefix) => {
|
|
1908
|
-
const prop = prefix.split('.')[1];
|
|
1909
|
-
// don't overwrite existing restrictions
|
|
1910
|
-
if(!navPropEntry[prop]) {
|
|
1911
|
-
// Filter properties with prefix and reduce them into a new dictionary
|
|
1912
|
-
const o = props.filter(p => p[0].startsWith(prefix+'.')).reduce((a,c) => {
|
|
1913
|
-
a[c[0].replace(prefix+'.', '')] = c[1];
|
|
1914
|
-
return a;
|
|
1915
|
-
}, { });
|
|
1916
|
-
// if dictionary has entries, add them to navPropEnty
|
|
1917
|
-
if(Object.keys(o).length) {
|
|
1918
|
-
// ReadRestrictions may have sub type ReadByKeyRestrictions { Description, LongDescription }
|
|
1919
|
-
// chop annotations into dictionaries
|
|
1920
|
-
if(prefix === '@Capabilities.ReadRestrictions' &&
|
|
1921
|
-
Object.keys(o).some(k => k.startsWith('ReadByKeyRestrictions.'))) {
|
|
1922
|
-
const no = {};
|
|
1923
|
-
Object.entries(o).forEach(([k,v]) => {
|
|
1924
|
-
const [head, ...tail] = k.split('.');
|
|
1925
|
-
if(head === 'ReadByKeyRestrictions' && tail.length) {
|
|
1926
|
-
if(!no['ReadByKeyRestrictions'])
|
|
1927
|
-
no['ReadByKeyRestrictions'] = {};
|
|
1928
|
-
// Don't try to add entry into non object
|
|
1929
|
-
if(typeof no['ReadByKeyRestrictions'] === 'object')
|
|
1930
|
-
no['ReadByKeyRestrictions'][tail.join('.')] = v;
|
|
1931
|
-
}
|
|
1932
|
-
else {
|
|
1933
|
-
no[k] = v;
|
|
1934
|
-
}
|
|
1935
|
-
});
|
|
1936
|
-
navPropEntry[prop] = no;
|
|
1937
|
-
}
|
|
1938
|
-
else {
|
|
1939
|
-
navPropEntry[prop] = o;
|
|
1940
|
-
}
|
|
1941
|
-
newEntry = true;
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
merge('@Capabilities.DeleteRestrictions');
|
|
1946
|
-
merge('@Capabilities.InsertRestrictions');
|
|
1947
|
-
merge('@Capabilities.UpdateRestrictions');
|
|
1948
|
-
merge('@Capabilities.ReadRestrictions');
|
|
1949
|
-
|
|
1950
|
-
if(newEntry) {
|
|
1951
|
-
if(!hasEntry) {
|
|
1952
|
-
if(!resProps)
|
|
1953
|
-
resProps = container[anno] = [ ];
|
|
1954
|
-
resProps.push(navPropEntry);
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
1994
|
function mapCdsToEdmProp(obj) {
|
|
1961
|
-
if (obj.type && isBuiltinType(obj.type) && !
|
|
1995
|
+
if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
|
|
1962
1996
|
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
1963
|
-
assignProp(obj, '_edmType', edmType);
|
|
1997
|
+
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
1964
1998
|
} else if (obj._isCollection && (obj.items && isBuiltinType(csnUtils.getFinalTypeDef(obj.items.type)))) {
|
|
1965
1999
|
let edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType'], obj.$path);
|
|
1966
|
-
assignProp(obj, '_edmType', edmType);
|
|
2000
|
+
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
1967
2001
|
}
|
|
1968
2002
|
// This is the special case when we have array of array, but will not be supported in the future
|
|
1969
2003
|
else if (obj._isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(csnUtils.getFinalTypeDef(obj.items.items.type))) {
|
|
1970
2004
|
let edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
1971
|
-
assignProp(obj, '_edmType', edmType);
|
|
2005
|
+
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
1972
2006
|
}
|
|
1973
2007
|
}
|
|
1974
2008
|
|
|
@@ -1986,375 +2020,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1986
2020
|
}
|
|
1987
2021
|
// it is a computed value if it is not a simple value or an annotation
|
|
1988
2022
|
if(!((def.val !== undefined && !noTailExpr) || def['#'])) {
|
|
1989
|
-
assignAnnotation(member, '@Core.ComputedDefaultValue', true);
|
|
2023
|
+
edmUtils.assignAnnotation(member, '@Core.ComputedDefaultValue', true);
|
|
1990
2024
|
}
|
|
1991
2025
|
}
|
|
1992
2026
|
}
|
|
1993
2027
|
}
|
|
1994
2028
|
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
/*
|
|
1999
|
-
* Late application specific transformations
|
|
2000
|
-
* At present there are two transformation targets: Structure and Element
|
|
2001
|
-
* These transformations are available today:
|
|
2002
|
-
*
|
|
2003
|
-
* Analytical Scenario:
|
|
2004
|
-
* If a structure is annotated with @Aggregation.ApplySupported.PropertyRestrictions
|
|
2005
|
-
* then a number of annotation rewrites are done to this structure and to the
|
|
2006
|
-
* elements of this structure
|
|
2007
|
-
* Also the key properties of all structure elements are removed and a new
|
|
2008
|
-
* artificial key element 'key _ID : String' is inserted at first position of
|
|
2009
|
-
* the elements dictionary
|
|
2010
|
-
*
|
|
2011
|
-
* PDM (Personal Data Management)
|
|
2012
|
-
* Planned but not yet implemented annotation rewriting (pending to finalization)
|
|
2013
|
-
* /
|
|
2014
|
-
|
|
2015
|
-
/* eslint max-statements-per-line:off */
|
|
2016
|
-
|
|
2017
|
-
function mapAnnotationAssignment(artifact, parent, mappingDictionary)
|
|
2018
|
-
{
|
|
2019
|
-
let props = intersect(Object.keys(mappingDictionary), Object.keys(artifact));
|
|
2020
|
-
// now start the substitution
|
|
2021
|
-
props.forEach(prop => {
|
|
2022
|
-
let [ mapping, value, remove_original ] = mappingDictionary[prop];
|
|
2023
|
-
if(mapping instanceof Function)
|
|
2024
|
-
{
|
|
2025
|
-
mapping(artifact, parent, prop);
|
|
2026
|
-
}
|
|
2027
|
-
else
|
|
2028
|
-
{
|
|
2029
|
-
assignAnnotation(artifact, mapping, value || artifact[prop]['='] || artifact[prop]);
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
if(remove_original)
|
|
2033
|
-
delete artifact[prop];
|
|
2034
|
-
});
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
function applyAppSpecificLateCsnTransformationOnElement(options, element, struct, error)
|
|
2038
|
-
{
|
|
2039
|
-
if(options.isV2())
|
|
2040
|
-
{
|
|
2041
|
-
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
2042
|
-
{
|
|
2043
|
-
mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
|
|
2044
|
-
}
|
|
2045
|
-
mapAnnotationAssignment(element, struct, PDMSemantics());
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
// etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
|
|
2049
|
-
// Oliver Heinrich mentions in the issue that the Okra runtime must be set to a
|
|
2050
|
-
// concurrent runtime mode by the caller, if the annotation is added this late,
|
|
2051
|
-
// it doesn't appear in the forOData processed CSN, meaning that the
|
|
2052
|
-
// runtime cannot set that okra flag (alternatively the runtime has to search
|
|
2053
|
-
// for @[odata|cds].etag annotations...
|
|
2054
|
-
if(options.isV4())
|
|
2055
|
-
{
|
|
2056
|
-
if(element['@odata.etag'] == true || element['@cds.etag'] == true) {
|
|
2057
|
-
// don't put element name into collection as per advice from Ralf Handl, as
|
|
2058
|
-
// no runtime is interested in the property itself, it is sufficient to mark
|
|
2059
|
-
// the entity set.
|
|
2060
|
-
assignAnnotation(struct, '@Core.OptimisticConcurrency',
|
|
2061
|
-
(struct['@Core.OptimisticConcurrency'] || [])/*.push(element.name)*/);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
// nested functions begin
|
|
2066
|
-
function PDMSemantics()
|
|
2067
|
-
{
|
|
2068
|
-
/*
|
|
2069
|
-
let dict = Object.create(null);
|
|
2070
|
-
|
|
2071
|
-
dict['@PDM.xxx1'] = [ '@sap.pdm-semantics' ];
|
|
2072
|
-
dict['@PDM.xxx2'] = [ '@sap.pdm-propery' ];
|
|
2073
|
-
dict['@PDM.xxx3'] = [ '@sap.pdm-display-sq-no' ];
|
|
2074
|
-
dict['@PDM.xxx4'] = [ '@sap.pdm-record-identifier' ];
|
|
2075
|
-
dict['@PDM.xxx5'] = [ '@sap.pdm-field-group' ];
|
|
2076
|
-
dict['@PDM.xxx6'] = [ '@sap.pdm-mask-find-pattern' ];
|
|
2077
|
-
dict['@PDM.xxx7'] = [ '@sap.pdm-mask-replacement-pattern' ];
|
|
2078
|
-
dict['@PDM.xxx8'] = [ '@sap.deletable' ];
|
|
2079
|
-
dict['@PDM.xxx8'] = [ '@sap.updatable' ];
|
|
2080
|
-
|
|
2081
|
-
// respect flattened annotation $value
|
|
2082
|
-
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
2083
|
-
*/
|
|
2084
|
-
return Object.create(null);
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
function AnalyticalAnnotations()
|
|
2088
|
-
{
|
|
2089
|
-
function mapCommonAttributes(element, struct, prop)
|
|
2090
|
-
{
|
|
2091
|
-
let CommonAttributes = element[prop];
|
|
2092
|
-
if(!Array.isArray(CommonAttributes)) {
|
|
2093
|
-
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
2094
|
-
{ anno: '@Common.attribute', code: JSON.stringify(CommonAttributes) },
|
|
2095
|
-
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
2096
|
-
return;
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
let targets = intersect(CommonAttributes, Object.keys(struct.elements));
|
|
2100
|
-
targets.forEach(tgt => {
|
|
2101
|
-
assignAnnotation(struct.elements[tgt], '@sap.attribute-for', element.name);
|
|
2102
|
-
});
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
function mapContextDefiningProperties(element, struct, prop)
|
|
2106
|
-
{
|
|
2107
|
-
let ContextDefiningProperties = element[prop];
|
|
2108
|
-
if(!Array.isArray(ContextDefiningProperties)) {
|
|
2109
|
-
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
2110
|
-
{ anno: '@Aggregation.ContextDefiningProperties', code: JSON.stringify(ContextDefiningProperties) },
|
|
2111
|
-
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
2112
|
-
return;
|
|
2113
|
-
}
|
|
2114
|
-
if(ContextDefiningProperties.length > 0)
|
|
2115
|
-
assignAnnotation(element, '@sap.super-ordinate', ContextDefiningProperties[ContextDefiningProperties.length-1]);
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
|
-
let dict = Object.create(null);
|
|
2119
|
-
//analytics term definition unknown, lower case
|
|
2120
|
-
dict['@Analytics.Measure'] = [ '@sap.aggregation-role', 'measure' ];
|
|
2121
|
-
dict['@Analytics.Dimension'] = [ '@sap.aggregation-role', 'dimension' ];
|
|
2122
|
-
dict['@Semantics.currencyCode'] = [ '@sap.semantics', 'currency-code', true ];
|
|
2123
|
-
dict['@Semantics.unitOfMeasure'] = [ '@sap.semantics', 'unit-of-measure', true ];
|
|
2124
|
-
|
|
2125
|
-
dict['@Measures.ISOCurrency'] = [ '@sap.unit' ];
|
|
2126
|
-
dict['@Measures.Unit'] = [ '@sap.unit' ];
|
|
2127
|
-
|
|
2128
|
-
dict['@Common.Label'] = [ '@sap.label' ];
|
|
2129
|
-
dict['@Common.Text'] = [ '@sap.text' ];
|
|
2130
|
-
dict['@Aggregation.ContextDefiningProperties'] = [ mapContextDefiningProperties ];
|
|
2131
|
-
dict['@Common.Attributes'] = [ mapCommonAttributes ];
|
|
2132
|
-
|
|
2133
|
-
// respect flattened annotation $value
|
|
2134
|
-
Object.entries(dict).forEach(([k, v]) => dict[k+'.$value'] = v);
|
|
2135
|
-
return dict;
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
|
-
|
|
2139
|
-
function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error)
|
|
2140
|
-
{
|
|
2141
|
-
if(options.isV2())
|
|
2142
|
-
{
|
|
2143
|
-
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
2144
|
-
{
|
|
2145
|
-
transformAnalyticalModel(struct);
|
|
2146
|
-
mapAnnotationAssignment(struct, undefined, AnalyticalAnnotations());
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
// nested functions begin
|
|
2151
|
-
function transformAnalyticalModel(struct)
|
|
2152
|
-
{
|
|
2153
|
-
let keyName = 'ID__';
|
|
2154
|
-
if(struct == undefined || struct.elements == undefined || struct.elements[keyName] != undefined)
|
|
2155
|
-
return;
|
|
2156
|
-
|
|
2157
|
-
// remove key prop from elements, add new key to elements
|
|
2158
|
-
let elements = Object.create(null);
|
|
2159
|
-
let key = { name: keyName, key : true, type : 'cds.String', '@sap.sortable':false, '@sap.filterable':false, '@UI.Hidden': true };
|
|
2160
|
-
elements[keyName] = key;
|
|
2161
|
-
setProp(struct, '$keys',{ [keyName] : key } );
|
|
2162
|
-
forEachGeneric(struct.items || struct, 'elements', (e,n) =>
|
|
2163
|
-
{
|
|
2164
|
-
if(e.key) delete e.key;
|
|
2165
|
-
elements[n] = e;
|
|
2166
|
-
});
|
|
2167
|
-
struct.elements = elements;
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
function AnalyticalAnnotations()
|
|
2171
|
-
{
|
|
2172
|
-
function mapFilterRestrictions(struct, parent, prop)
|
|
2173
|
-
{
|
|
2174
|
-
let stringDict = Object.create(null);
|
|
2175
|
-
stringDict['SingleValue'] = 'single-value';
|
|
2176
|
-
stringDict['MultiValue'] = 'multi-value';
|
|
2177
|
-
stringDict['SingleRange'] = 'interval';
|
|
2178
|
-
|
|
2179
|
-
let filterRestrictions = struct[prop];
|
|
2180
|
-
if(!Array.isArray(filterRestrictions)) {
|
|
2181
|
-
error(null, ['definitions', struct.name ],
|
|
2182
|
-
{ anno: '@Capabilities.FilterRestrictions.FilterExpressionRestrictions',
|
|
2183
|
-
code: JSON.stringify(filterRestrictions) },
|
|
2184
|
-
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
2185
|
-
return;
|
|
2186
|
-
}
|
|
2187
|
-
filterRestrictions.forEach(v => {
|
|
2188
|
-
let e = struct.elements[v.Property];
|
|
2189
|
-
if(e)
|
|
2190
|
-
assignAnnotation(e, '@sap.filter-restriction', stringDict[v.AllowedExpressions]);
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
function mapRequiredProperties(struct, parent, prop)
|
|
2195
|
-
{
|
|
2196
|
-
let requiredProperties = struct[prop];
|
|
2197
|
-
if(!Array.isArray(requiredProperties)) {
|
|
2198
|
-
error(null, ['definitions', struct.name],
|
|
2199
|
-
{ anno: '@Capabilities.FilterRestrictions.RequiredProperties',
|
|
2200
|
-
code: JSON.stringify(requiredProperties) },
|
|
2201
|
-
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
2202
|
-
return;
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
|
-
let props = intersect(Object.keys(struct.elements), requiredProperties)
|
|
2206
|
-
props.forEach(p => {
|
|
2207
|
-
assignAnnotation(struct.elements[p], '@sap.required-in-filter', true);
|
|
2208
|
-
});
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
function mapRequiresFilter(struct, parent, prop)
|
|
2212
|
-
{
|
|
2213
|
-
let requiresFilter = struct[prop];
|
|
2214
|
-
if(requiresFilter)
|
|
2215
|
-
assignAnnotation(struct._SetAttributes, '@sap.requires-filter', requiresFilter);
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
// Entity Props
|
|
2219
|
-
let dict = Object.create(null);
|
|
2220
|
-
dict['@Aggregation.ApplySupported.PropertyRestrictions'] = [ '@sap.semantics', 'aggregate' ];
|
|
2221
|
-
dict['@Common.Label'] = [ '@sap.label' ];
|
|
2222
|
-
dict['@Capabilities.FilterRestrictions.RequiresFilter'] = [ mapRequiresFilter ];
|
|
2223
|
-
dict['@Capabilities.FilterRestrictions.RequiredProperties'] = [ mapRequiredProperties ];
|
|
2224
|
-
dict['@Capabilities.FilterRestrictions.FilterExpressionRestrictions'] = [ mapFilterRestrictions ];
|
|
2225
|
-
|
|
2226
|
-
// respect flattened annotation $value
|
|
2227
|
-
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
2228
|
-
|
|
2229
|
-
return dict;
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
function setSAPSpecificV2AnnotationsToEntityContainer(options, carrier) {
|
|
2234
|
-
if(!options.isV2())
|
|
2235
|
-
return;
|
|
2236
|
-
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntityContainer
|
|
2237
|
-
const SetAttributes = {
|
|
2238
|
-
// EntityContainer only
|
|
2239
|
-
'@sap.supported.formats' : addToSetAttr,
|
|
2240
|
-
'@sap.use.batch': addToSetAttr,
|
|
2241
|
-
'@sap.message.scope.supported': addToSetAttr,
|
|
2242
|
-
};
|
|
2243
|
-
|
|
2244
|
-
Object.entries(carrier).forEach(([p, v]) => {
|
|
2245
|
-
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
2246
|
-
});
|
|
2247
|
-
|
|
2248
|
-
function addToSetAttr(carrier, propName, propValue, removeFromType=true) {
|
|
2249
|
-
assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
2250
|
-
assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
2251
|
-
if(removeFromType) {
|
|
2252
|
-
delete carrier[propName];
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
2258
|
-
if(!options.isV2())
|
|
2259
|
-
return;
|
|
2260
|
-
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntitySet
|
|
2261
|
-
const SetAttributes = {
|
|
2262
|
-
// EntitySet, EntityType
|
|
2263
|
-
'@sap.label' : (s,pn, pv) => { addToSetAttr(s, pn, pv, false); },
|
|
2264
|
-
'@sap.semantics': checkSemantics,
|
|
2265
|
-
// EntitySet only
|
|
2266
|
-
'@sap.creatable' : addToSetAttr,
|
|
2267
|
-
'@sap.updatable' : addToSetAttr,
|
|
2268
|
-
'@sap.deletable': addToSetAttr,
|
|
2269
|
-
'@sap.updatable.path': addToSetAttr,
|
|
2270
|
-
'@sap.deletable.path': addToSetAttr,
|
|
2271
|
-
'@sap.searchable' : addToSetAttr,
|
|
2272
|
-
'@sap.pagable': addToSetAttr,
|
|
2273
|
-
'@sap.topable': addToSetAttr,
|
|
2274
|
-
'@sap.countable': addToSetAttr,
|
|
2275
|
-
'@sap.addressable': addToSetAttr,
|
|
2276
|
-
'@sap.requires.filter': addToSetAttr,
|
|
2277
|
-
'@sap.change.tracking': addToSetAttr,
|
|
2278
|
-
'@sap.maxpagesize': addToSetAttr,
|
|
2279
|
-
'@sap.delta.link.validity': addToSetAttr,
|
|
2280
|
-
};
|
|
2281
|
-
|
|
2282
|
-
Object.entries(carrier).forEach(([p, v]) => {
|
|
2283
|
-
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
2284
|
-
});
|
|
2285
|
-
|
|
2286
|
-
function addToSetAttr(carrier, propName, propValue, removeFromType=true) {
|
|
2287
|
-
assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
2288
|
-
assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
2289
|
-
if(removeFromType) {
|
|
2290
|
-
delete carrier[propName];
|
|
2291
|
-
}
|
|
2292
|
-
}
|
|
2293
|
-
|
|
2294
|
-
function checkSemantics(struct, propName, propValue) {
|
|
2295
|
-
if(propValue === 'timeseries' || propValue === 'aggregate') {
|
|
2296
|
-
// aggregate is forwarded to Set and must remain on Type
|
|
2297
|
-
addToSetAttr(struct, propName, propValue, propValue !== 'aggregate');
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
|
|
2302
|
-
function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
2303
|
-
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0
|
|
2304
|
-
const SetAttributes = {
|
|
2305
|
-
// Applicable to NavProp and foreign keys, add to AssociationSet
|
|
2306
|
-
'@sap.creatable' : (c, pn, pv) => { addToAssociationSet(c, pn, pv, false); },
|
|
2307
|
-
// Not applicable to NavProp, applicable to foreign keys, add to AssociationSet
|
|
2308
|
-
'@sap.updatable' : addToAssociationSet,
|
|
2309
|
-
// Not applicable to NavProp, not applicable to foreign key, add to AssociationSet
|
|
2310
|
-
'@sap.deletable': (c, pn, pv) => {
|
|
2311
|
-
addToAssociationSet(c, pn, pv);
|
|
2312
|
-
removeFromForeignKey(c, pn);
|
|
2313
|
-
},
|
|
2314
|
-
// applicable to NavProp, not applicable to foreign keys, not applicable to AssociationSet
|
|
2315
|
-
'@sap.creatable.path': removeFromForeignKey,
|
|
2316
|
-
'@sap.filterable': removeFromForeignKey,
|
|
2317
|
-
};
|
|
2318
|
-
|
|
2319
|
-
Object.entries(carrier).forEach(([p, v]) => {
|
|
2320
|
-
(SetAttributes[p] || function() {/* no-op */})(carrier, p, v);
|
|
2321
|
-
});
|
|
2322
|
-
|
|
2323
|
-
function addToAssociationSet(carrier, propName, propValue, removeFromType=true) {
|
|
2324
|
-
if(isAssociationOrComposition(carrier)) {
|
|
2325
|
-
assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
2326
|
-
assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
2327
|
-
if(removeFromType) {
|
|
2328
|
-
delete carrier[propName];
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
|
|
2333
|
-
function removeFromForeignKey(carrier, propName) {
|
|
2334
|
-
if(carrier['@odata.foreignKey4'] && carrier[propName] !== undefined) {
|
|
2335
|
-
delete carrier[propName];
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
|
-
// Assign but not overwrite annotation
|
|
2341
|
-
function assignAnnotation(node, name, value) {
|
|
2342
|
-
if(value !== undefined &&
|
|
2343
|
-
name !== undefined && name[0] === '@' &&
|
|
2344
|
-
(node[name] === undefined ||
|
|
2345
|
-
node[name] && node[name] === null)) {
|
|
2346
|
-
node[name] = value;
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
// Set non enumerable property if it doesn't exist yet
|
|
2351
|
-
function assignProp(obj, prop, value) {
|
|
2352
|
-
if(obj[prop] === undefined) {
|
|
2353
|
-
setProp(obj, prop, value);
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
2029
|
module.exports = {
|
|
2358
2030
|
initializeModel,
|
|
2359
|
-
assignAnnotation
|
|
2360
2031
|
}
|