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