@sap/cds-compiler 4.1.2 → 4.2.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 +101 -1
- package/bin/cdsc.js +6 -3
- package/doc/CHANGELOG_BETA.md +5 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +2 -2
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +24 -24
- package/lib/base/message-registry.js +41 -6
- package/lib/base/messages.js +7 -0
- package/lib/base/model.js +37 -8
- package/lib/checks/elements.js +11 -10
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +5 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -3
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/utils.js +3 -2
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +27 -24
- package/lib/compiler/base.js +6 -2
- package/lib/compiler/builtins.js +34 -34
- package/lib/compiler/checks.js +179 -208
- package/lib/compiler/classes.js +2 -2
- package/lib/compiler/cycle-detector.js +6 -6
- package/lib/compiler/define.js +66 -45
- package/lib/compiler/extend.js +81 -72
- package/lib/compiler/finalize-parse-cdl.js +26 -26
- package/lib/compiler/generate.js +61 -45
- package/lib/compiler/index.js +47 -49
- package/lib/compiler/kick-start.js +8 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +42 -35
- package/lib/compiler/propagator.js +6 -6
- package/lib/compiler/resolve.js +170 -126
- package/lib/compiler/shared.js +122 -45
- package/lib/compiler/tweak-assocs.js +93 -40
- package/lib/compiler/utils.js +15 -12
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +678 -772
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +686 -646
- package/lib/edm/edmUtils.js +277 -296
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1253 -1276
- package/lib/json/from-csn.js +34 -4
- package/lib/json/to-csn.js +4 -4
- package/lib/language/language.g4 +2 -5
- package/lib/main.d.ts +61 -1
- package/lib/model/csnUtils.js +31 -2
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/modelCompare/compare.js +37 -2
- package/lib/modelCompare/utils/filter.js +1 -1
- package/lib/optionProcessor.js +15 -3
- package/lib/render/toCdl.js +30 -4
- package/lib/render/toSql.js +5 -9
- package/lib/render/utils/common.js +8 -6
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +121 -47
- package/lib/transform/db/flattening.js +75 -7
- package/lib/transform/forOdata.js +4 -1
- package/lib/transform/forRelationalDB.js +80 -62
- package/lib/transform/localized.js +91 -54
- package/lib/transform/transformUtils.js +9 -10
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
|
|
2
3
|
/* eslint max-statements-per-line:off */
|
|
3
4
|
const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
4
5
|
const {
|
|
@@ -15,10 +16,10 @@ const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
|
|
|
15
16
|
const NavResAnno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
|
|
16
17
|
|
|
17
18
|
// Capabilities that can be pulled up to NavigationRestrictions
|
|
18
|
-
const capabilities = Object.keys(require('../gen/Dictionary.json')
|
|
19
|
-
types['Capabilities.NavigationPropertyRestriction'].Properties)
|
|
20
|
-
filter(c => !['NavigationProperty', 'Navigability'].includes(c))
|
|
21
|
-
map(c =>
|
|
19
|
+
const capabilities = Object.keys(require('../gen/Dictionary.json')
|
|
20
|
+
.types['Capabilities.NavigationPropertyRestriction'].Properties)
|
|
21
|
+
.filter(c => ![ 'NavigationProperty', 'Navigability' ].includes(c))
|
|
22
|
+
.map(c => `@Capabilities.${c}`);
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* edmPreprocessor warms up the model so that it can be converted into an EDM document and
|
|
@@ -30,9 +31,10 @@ const capabilities = Object.keys(require('../gen/Dictionary.json').
|
|
|
30
31
|
* @param {CSN.Model} csn
|
|
31
32
|
* @param {object} _options
|
|
32
33
|
*/
|
|
33
|
-
function initializeModel(csn, _options, messageFunctions, requestedServiceNames=undefined)
|
|
34
|
-
{
|
|
35
|
-
|
|
34
|
+
function initializeModel( csn, _options, messageFunctions, requestedServiceNames = undefined ) {
|
|
35
|
+
const {
|
|
36
|
+
info, warning, error, message,
|
|
37
|
+
} = messageFunctions;
|
|
36
38
|
const special$self = !csn?.definitions?.$self && '$self';
|
|
37
39
|
const csnUtils = getUtils(csn);
|
|
38
40
|
|
|
@@ -45,36 +47,36 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
// make sure options are complete
|
|
48
|
-
|
|
50
|
+
const options = edmUtils.validateOptions(_options);
|
|
49
51
|
|
|
50
52
|
const [ serviceRoots,
|
|
51
53
|
serviceRootNames,
|
|
52
54
|
fallBackSchemaName,
|
|
53
|
-
whatsMyServiceRootName ] = getAnOverviewOnTheServices(
|
|
55
|
+
whatsMyServiceRootName ] = getAnOverviewOnTheServices();
|
|
56
|
+
|
|
57
|
+
if (serviceRootNames.length === 0)
|
|
58
|
+
return [ serviceRoots, Object.create(null), reqDefs, whatsMyServiceRootName, fallBackSchemaName, options ];
|
|
54
59
|
|
|
55
|
-
if(serviceRootNames.length === 0) {
|
|
56
|
-
return [serviceRoots, Object.create(null), reqDefs, whatsMyServiceRootName, fallBackSchemaName, options];
|
|
57
|
-
}
|
|
58
60
|
|
|
59
|
-
if(requestedServiceNames === undefined)
|
|
61
|
+
if (requestedServiceNames === undefined)
|
|
60
62
|
requestedServiceNames = options.serviceNames;
|
|
61
|
-
if(requestedServiceNames === undefined)
|
|
63
|
+
if (requestedServiceNames === undefined)
|
|
62
64
|
requestedServiceNames = serviceRootNames;
|
|
63
|
-
}
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
|
|
67
|
+
function isMyServiceRequested( n ) {
|
|
66
68
|
return requestedServiceNames.includes(whatsMyServiceRootName(n));
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
// Structural CSN inbound QA checks
|
|
70
72
|
inboundQualificationChecks(csn, options, messageFunctions,
|
|
71
|
-
|
|
73
|
+
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName, csnUtils);
|
|
72
74
|
// not needed at the moment
|
|
73
75
|
// resolveForeignKeyRefs();
|
|
74
76
|
|
|
75
|
-
if(isBetaEnabled(options, undefined))
|
|
77
|
+
if (isBetaEnabled(options, undefined))
|
|
76
78
|
splitDottedDefinitionsIntoSeparateServices();
|
|
77
|
-
|
|
79
|
+
|
|
78
80
|
else
|
|
79
81
|
/*
|
|
80
82
|
Replace dots with underscores for all definitions below a context
|
|
@@ -94,7 +96,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
94
96
|
as a step in the OData transformer with the goal to have a protocol agnostic OData CSN.
|
|
95
97
|
*/
|
|
96
98
|
if (csn.meta && csn.meta.options && csn.meta.options.odataVersion === 'v4' && options.isV2()) {
|
|
97
|
-
|
|
99
|
+
// eslint-disable-next-line global-require
|
|
100
|
+
const { toFinalBaseType } = require('../transform/transformUtils').getTransformers(csn, options);
|
|
98
101
|
expandCSNToFinalBaseType(csn, { toFinalBaseType }, csnUtils, serviceRootNames, options);
|
|
99
102
|
}
|
|
100
103
|
|
|
@@ -102,31 +105,33 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
102
105
|
Enrich the CSN by de-anonymizing and exposing types that are required to make the service self contained.
|
|
103
106
|
*/
|
|
104
107
|
const schemas = typesExposure(csn, whatsMyServiceRootName, requestedServiceNames,
|
|
105
|
-
|
|
108
|
+
fallBackSchemaName, options, csnUtils, { error });
|
|
106
109
|
|
|
107
110
|
// Get an overview about all schemas (including the services)
|
|
108
|
-
const schemaNames = [...serviceRootNames];
|
|
111
|
+
const schemaNames = [ ...serviceRootNames ];
|
|
109
112
|
schemaNames.push(...Object.keys(schemas));
|
|
110
113
|
// sort schemas in reverse order to allow longest match in whatsMySchemaName function
|
|
111
|
-
schemaNames.sort((a,b) => b.length-a.length);
|
|
112
|
-
function whatsMySchemaName(n) {
|
|
113
|
-
return schemaNames.reduce((rc, sn) => !rc && n && n.startsWith(sn
|
|
114
|
+
schemaNames.sort((a, b) => b.length - a.length);
|
|
115
|
+
function whatsMySchemaName( n ) {
|
|
116
|
+
return schemaNames.reduce((rc, sn) => (!rc && n && n.startsWith(`${sn}.`) ? sn : rc), undefined);
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
if(schemaNames.length) {
|
|
119
|
+
if (schemaNames.length) {
|
|
117
120
|
forEachDefinition(csn, [
|
|
118
121
|
attachNameProperty,
|
|
119
122
|
(def, defName) => {
|
|
120
123
|
const mySchemaName = whatsMySchemaName(defName);
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
if (mySchemaName)
|
|
125
|
+
setProp(def, '$mySchemaName', mySchemaName);
|
|
126
|
+
if (isMyServiceRequested(defName) && def.kind !== 'aspect')
|
|
123
127
|
reqDefs.definitions[defName] = def;
|
|
124
128
|
},
|
|
125
|
-
linkAssociationTarget
|
|
129
|
+
linkAssociationTarget,
|
|
126
130
|
]);
|
|
127
131
|
forEachGeneric(csn, 'vocabularies', (term, termName) => {
|
|
128
132
|
const mySchemaName = whatsMySchemaName(termName);
|
|
129
|
-
|
|
133
|
+
if (mySchemaName)
|
|
134
|
+
setProp(term, '$mySchemaName', mySchemaName);
|
|
130
135
|
});
|
|
131
136
|
// initialize requested services
|
|
132
137
|
const skip = { skipArtifact: (_def, defName) => !isMyServiceRequested(defName) };
|
|
@@ -140,7 +145,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
140
145
|
// Initialize associations after _parent linking
|
|
141
146
|
forEachDefinition(reqDefs, initConstraints);
|
|
142
147
|
// Mute V4 elements depending on constraint preparation
|
|
143
|
-
if(options.isV4())
|
|
148
|
+
if (options.isV4())
|
|
144
149
|
forEachDefinition(reqDefs, ignoreProperties);
|
|
145
150
|
// calculate constraints based on ignoreProperties and initConstraints
|
|
146
151
|
forEachDefinition(reqDefs, finalizeConstraints);
|
|
@@ -151,19 +156,20 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
151
156
|
// Decide if an entity set needs to be constructed or not
|
|
152
157
|
forEachDefinition(reqDefs, [
|
|
153
158
|
exposeTargetsAsProxiesOrSchemaRefs,
|
|
154
|
-
determineEntitySet
|
|
159
|
+
determineEntitySet,
|
|
155
160
|
]);
|
|
156
161
|
// finalize proxy creation
|
|
157
162
|
mergeProxiesIntoModel();
|
|
158
163
|
|
|
159
164
|
// Calculate NavPropBinding Target paths
|
|
160
165
|
// Rewrite @Capabilities for containment mode
|
|
161
|
-
if(options.isV4())
|
|
166
|
+
if (options.isV4()) {
|
|
162
167
|
forEachDefinition(reqDefs, [
|
|
163
168
|
initEdmNavPropBindingTargets,
|
|
164
169
|
pullupCapabilitiesAnnotations,
|
|
165
|
-
annotateOptionalActFuncParams
|
|
170
|
+
annotateOptionalActFuncParams,
|
|
166
171
|
]);
|
|
172
|
+
}
|
|
167
173
|
|
|
168
174
|
// Things that can be done in one pass
|
|
169
175
|
// Create edmKeyRefPaths
|
|
@@ -172,7 +178,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
172
178
|
forEachDefinition(reqDefs, [
|
|
173
179
|
initEdmKeyRefPaths,
|
|
174
180
|
initEdmNavPropBindingPaths,
|
|
175
|
-
finalize
|
|
181
|
+
finalize,
|
|
176
182
|
]);
|
|
177
183
|
}
|
|
178
184
|
return [
|
|
@@ -181,47 +187,47 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
181
187
|
reqDefs,
|
|
182
188
|
whatsMyServiceRootName,
|
|
183
189
|
fallBackSchemaName,
|
|
184
|
-
options
|
|
190
|
+
options,
|
|
185
191
|
];
|
|
186
192
|
|
|
187
|
-
|
|
193
|
+
// ////////////////////////////////////////////////////////////////////
|
|
188
194
|
//
|
|
189
195
|
// Service initialization starts here
|
|
190
196
|
//
|
|
191
197
|
|
|
192
|
-
function getAnOverviewOnTheServices(
|
|
198
|
+
function getAnOverviewOnTheServices( ) {
|
|
193
199
|
const defs = csn.definitions || {};
|
|
194
|
-
const
|
|
195
|
-
for(const defName in defs) {
|
|
200
|
+
const sroots = Object.create(null);
|
|
201
|
+
for (const defName in defs) {
|
|
196
202
|
const def = defs[defName];
|
|
197
|
-
if(def && def.kind === 'service')
|
|
198
|
-
|
|
203
|
+
if (def && def.kind === 'service')
|
|
204
|
+
sroots[defName] = Object.assign(def, { name: defName });
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
// first of all we need to know about all 'real' user defined services
|
|
202
|
-
const
|
|
208
|
+
const srootNames = Object.keys(sroots).sort((a, b) => b.length - a.length);
|
|
203
209
|
|
|
204
|
-
function whatsMyServiceRootName(n, self=true) {
|
|
205
|
-
return serviceRootNames.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') || (n === sn && self) ? sn : rc, undefined);
|
|
206
|
-
}
|
|
207
210
|
|
|
208
211
|
// find a globally unambiguous schema name to collect all top level 'root' types
|
|
209
212
|
// TODO: work on service basis (this requires post exposure renaming)
|
|
210
|
-
let
|
|
213
|
+
let fbSchemaName = 'root';
|
|
211
214
|
let i = 1;
|
|
212
215
|
const defNames = Object.keys(defs);
|
|
213
|
-
|
|
216
|
+
// eslint-disable-next-line no-loop-func
|
|
217
|
+
while (defNames.some((artName) => {
|
|
214
218
|
const p = artName.split('.');
|
|
215
|
-
return p.length === 2 && p[0] ===
|
|
216
|
-
}))
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
return p.length === 2 && p[0] === fbSchemaName;
|
|
220
|
+
}))
|
|
221
|
+
fbSchemaName = `root${i++}`;
|
|
222
|
+
|
|
219
223
|
|
|
220
224
|
return [
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
whatsMyServiceRootName
|
|
225
|
+
sroots,
|
|
226
|
+
srootNames,
|
|
227
|
+
fbSchemaName,
|
|
228
|
+
// whatsMyServiceRootName
|
|
229
|
+
( n, self = true ) => srootNames.reduce((rc, sn) => (!rc && n && n.startsWith(`${sn}.`) || (n === sn && self) ? sn : rc), undefined),
|
|
230
|
+
];
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
/*
|
|
@@ -231,11 +237,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
231
237
|
All type refs and assoc targets must also be adjusted to refer to the new names.
|
|
232
238
|
*/
|
|
233
239
|
function renameDottedDefinitionsInsideServiceOrContext() {
|
|
234
|
-
|
|
235
240
|
// Find the first definition above the current definition or undefined otherwise.
|
|
236
241
|
// Definition can either be a context or a service
|
|
237
|
-
function getRootDef(name) {
|
|
238
|
-
const scopeKinds = {
|
|
242
|
+
function getRootDef( name ) {
|
|
243
|
+
const scopeKinds = { service: 1, context: 1 };
|
|
239
244
|
let pos = name.lastIndexOf('.');
|
|
240
245
|
name = pos < 0 ? undefined : name.substring(0, pos);
|
|
241
246
|
while (name && !((csn.definitions[name] && csn.definitions[name].kind) in scopeKinds)) {
|
|
@@ -247,24 +252,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
247
252
|
|
|
248
253
|
const dotEntityNameMap = Object.create(null);
|
|
249
254
|
const dotTypeNameMap = Object.create(null);
|
|
250
|
-
const kinds = {
|
|
255
|
+
const kinds = {
|
|
256
|
+
entity: 1, type: 1, action: 1, function: 1,
|
|
257
|
+
};
|
|
251
258
|
forEachDefinition(csn, (def, defName) => {
|
|
252
|
-
if(def.kind in kinds) {
|
|
259
|
+
if (def.kind in kinds) {
|
|
253
260
|
const rootDef = getRootDef(defName);
|
|
254
261
|
// if this definition has a root def and the root def is not the service/schema name
|
|
255
262
|
// => service C { type D.E }, replace the prefix dots with underscores
|
|
256
|
-
if(rootDef && defName !== rootDef && rootDef !== edmUtils.getSchemaPrefix(defName)) {
|
|
257
|
-
|
|
263
|
+
if (rootDef && defName !== rootDef && rootDef !== edmUtils.getSchemaPrefix(defName)) {
|
|
264
|
+
const newDefName = `${rootDef}.${defName.replace(`${rootDef}.`, '').replace(/\./g, '_')}`;
|
|
258
265
|
// store renamed types in correlation maps for later renaming
|
|
259
|
-
if(def.kind === 'entity')
|
|
266
|
+
if (def.kind === 'entity')
|
|
260
267
|
dotEntityNameMap[defName] = newDefName;
|
|
261
|
-
if(def.kind === 'type')
|
|
268
|
+
if (def.kind === 'type')
|
|
262
269
|
dotTypeNameMap[defName] = newDefName;
|
|
263
270
|
// rename in csn.definitions
|
|
264
271
|
const art = csn.definitions[newDefName];
|
|
265
|
-
if(art !== undefined) {
|
|
272
|
+
if (art !== undefined) {
|
|
266
273
|
error(null, [ 'definitions', defName ], { name: newDefName },
|
|
267
|
-
|
|
274
|
+
'Artifact name containing dots can\'t be mapped to an OData compliant name because it conflicts with existing definition $(NAME)');
|
|
268
275
|
}
|
|
269
276
|
else {
|
|
270
277
|
csn.definitions[newDefName] = def;
|
|
@@ -276,32 +283,34 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
276
283
|
});
|
|
277
284
|
// rename type refs to new type names
|
|
278
285
|
const rewrite = (def) => {
|
|
279
|
-
const applyOnNode = (node) => {
|
|
280
|
-
node = node.items || node;
|
|
281
|
-
if(node.type && dotTypeNameMap[node.type]) {
|
|
282
|
-
node.type = dotTypeNameMap[node.type];
|
|
283
|
-
}
|
|
284
|
-
if(node.target && dotEntityNameMap[node.target]) {
|
|
285
|
-
node.target = dotEntityNameMap[node.target];
|
|
286
|
-
}
|
|
287
|
-
if(node.$path && dotEntityNameMap[node.$path[1]]) {
|
|
288
|
-
node.$path[1] = dotEntityNameMap[node.$path[1]]
|
|
289
|
-
}
|
|
290
|
-
rewriteReferencesInActions(node);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
286
|
const rewriteReferencesInActions = (act) => {
|
|
294
|
-
|
|
295
|
-
param
|
|
296
|
-
|
|
297
|
-
param.type
|
|
298
|
-
|
|
299
|
-
|
|
287
|
+
if (act.params) {
|
|
288
|
+
Object.values(act.params).forEach((param) => {
|
|
289
|
+
param = param.items || param;
|
|
290
|
+
if (param.type && (dotEntityNameMap[param.type] || dotTypeNameMap[param.type]))
|
|
291
|
+
param.type = dotEntityNameMap[param.type] || dotTypeNameMap[param.type];
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (act.returns) {
|
|
300
295
|
const returnsObj = act.returns.items || act.returns;
|
|
301
296
|
if (returnsObj.type && dotEntityNameMap[returnsObj.type] || dotTypeNameMap[returnsObj.type])
|
|
302
297
|
returnsObj.type = dotEntityNameMap[returnsObj.type] || dotTypeNameMap[returnsObj.type];
|
|
303
298
|
}
|
|
304
|
-
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const applyOnNode = (node) => {
|
|
302
|
+
node = node.items || node;
|
|
303
|
+
if (node.type && dotTypeNameMap[node.type])
|
|
304
|
+
node.type = dotTypeNameMap[node.type];
|
|
305
|
+
|
|
306
|
+
if (node.target && dotEntityNameMap[node.target])
|
|
307
|
+
node.target = dotEntityNameMap[node.target];
|
|
308
|
+
|
|
309
|
+
if (node.$path && dotEntityNameMap[node.$path[1]])
|
|
310
|
+
node.$path[1] = dotEntityNameMap[node.$path[1]];
|
|
311
|
+
|
|
312
|
+
rewriteReferencesInActions(node);
|
|
313
|
+
};
|
|
305
314
|
|
|
306
315
|
forEachMemberRecursively(def, applyOnNode);
|
|
307
316
|
applyOnNode(def);
|
|
@@ -321,11 +330,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
321
330
|
*/
|
|
322
331
|
function splitDottedDefinitionsIntoSeparateServices() {
|
|
323
332
|
forEachDefinition(csn, (def, defName) => {
|
|
324
|
-
if(def.kind !== 'service') {
|
|
333
|
+
if (def.kind !== 'service') {
|
|
325
334
|
const myServiceRoot = whatsMyServiceRootName(defName);
|
|
326
335
|
const mySchemaPrefix = edmUtils.getSchemaPrefix(defName);
|
|
327
|
-
if(myServiceRoot && options.isV4() &&
|
|
328
|
-
/*(options.odataProxies || options.odataXServiceRefs) && options.isStructFormat && */
|
|
336
|
+
if (myServiceRoot && options.isV4() &&
|
|
337
|
+
/* (options.odataProxies || options.odataXServiceRefs) && options.isStructFormat && */
|
|
329
338
|
defName !== myServiceRoot && myServiceRoot !== mySchemaPrefix) {
|
|
330
339
|
const service = { kind: 'service', name: mySchemaPrefix };
|
|
331
340
|
serviceRoots[mySchemaPrefix] = service;
|
|
@@ -333,42 +342,42 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
333
342
|
}
|
|
334
343
|
}
|
|
335
344
|
});
|
|
336
|
-
serviceRootNames.sort((a,b) => b.length-a.length);
|
|
345
|
+
serviceRootNames.sort((a, b) => b.length - a.length);
|
|
337
346
|
}
|
|
338
347
|
|
|
339
|
-
function attachNameProperty(def, defName) {
|
|
340
|
-
edmUtils.assignProp
|
|
348
|
+
function attachNameProperty( def, defName ) {
|
|
349
|
+
edmUtils.assignProp(def, 'name', defName);
|
|
341
350
|
// Attach name to bound actions, functions and parameters
|
|
342
|
-
forEachGeneric(def, 'actions', (a,
|
|
343
|
-
edmUtils.assignProp(a, 'name',
|
|
344
|
-
forEachGeneric(a, 'params', (p,
|
|
345
|
-
edmUtils.assignProp(p, 'name',
|
|
351
|
+
forEachGeneric(def, 'actions', (a, an) => {
|
|
352
|
+
edmUtils.assignProp(a, 'name', an);
|
|
353
|
+
forEachGeneric(a, 'params', (p, pn) => {
|
|
354
|
+
edmUtils.assignProp(p, 'name', pn);
|
|
346
355
|
});
|
|
347
356
|
});
|
|
348
357
|
// Attach name unbound action parameters
|
|
349
|
-
forEachGeneric(def, 'params', (p,
|
|
350
|
-
edmUtils.assignProp(p, 'name',
|
|
358
|
+
forEachGeneric(def, 'params', (p, pn) => {
|
|
359
|
+
edmUtils.assignProp(p, 'name', pn);
|
|
351
360
|
});
|
|
352
361
|
}
|
|
353
362
|
|
|
354
363
|
// initialize the service itself
|
|
355
|
-
function initService(serviceRoot) {
|
|
364
|
+
function initService( serviceRoot ) {
|
|
356
365
|
edmAnnoPreproc.setSAPSpecificV2AnnotationsToEntityContainer(options, serviceRoot);
|
|
357
366
|
}
|
|
358
367
|
|
|
359
368
|
// link association target to association and add @odata.contained to compositions in V4
|
|
360
|
-
function linkAssociationTarget(struct) {
|
|
369
|
+
function linkAssociationTarget( struct ) {
|
|
361
370
|
forEachMemberRecursively(struct, (element, name, prop, subpath) => {
|
|
362
|
-
if(element.target && !element._target) {
|
|
363
|
-
|
|
364
|
-
if(target) {
|
|
371
|
+
if (element.target && !element._target) {
|
|
372
|
+
const target = csn.definitions[element.target];
|
|
373
|
+
if (target) {
|
|
365
374
|
setProp(element, '_target', target);
|
|
366
375
|
// If target has parameters, xref assoc at target for redirection
|
|
367
|
-
if(edmUtils.isParameterizedEntity(target)) {
|
|
368
|
-
if(!target.$sources)
|
|
376
|
+
if (edmUtils.isParameterizedEntity(target)) {
|
|
377
|
+
if (!target.$sources)
|
|
369
378
|
setProp(target, '$sources', Object.create(null));
|
|
370
|
-
|
|
371
|
-
target.$sources[struct.name
|
|
379
|
+
|
|
380
|
+
target.$sources[`${struct.name}.${name}`] = element;
|
|
372
381
|
}
|
|
373
382
|
}
|
|
374
383
|
else {
|
|
@@ -376,12 +385,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
376
385
|
}
|
|
377
386
|
}
|
|
378
387
|
// in V4 tag all compositions to be containments
|
|
379
|
-
if(options.odataContainment &&
|
|
388
|
+
if (options.odataContainment &&
|
|
380
389
|
options.isV4() &&
|
|
381
390
|
csnUtils.isComposition(element) &&
|
|
382
|
-
element['@odata.contained'] === undefined)
|
|
391
|
+
element['@odata.contained'] === undefined)
|
|
383
392
|
element['@odata.contained'] = true;
|
|
384
|
-
}
|
|
385
393
|
});
|
|
386
394
|
}
|
|
387
395
|
|
|
@@ -401,21 +409,21 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
401
409
|
// $containeeAssociations stores the containees (children/outbound edges)
|
|
402
410
|
// $containerNames stores the containers (parents/inbound edges)
|
|
403
411
|
|
|
404
|
-
function initContainments(container) {
|
|
405
|
-
if(container.kind === 'entity') {
|
|
406
|
-
if(!container.$containeeAssociations)
|
|
412
|
+
function initContainments( container ) {
|
|
413
|
+
if (container.kind === 'entity') {
|
|
414
|
+
if (!container.$containeeAssociations)
|
|
407
415
|
setProp(container, '$containeeAssociations', []);
|
|
408
|
-
forEachMemberRecursively(container,
|
|
409
|
-
|
|
416
|
+
forEachMemberRecursively(container, eachAssoc,
|
|
417
|
+
[], true, { pathWithoutProp: true, elementsOnly: true });
|
|
410
418
|
}
|
|
411
419
|
|
|
412
|
-
function
|
|
413
|
-
if(elt.target && elt['@odata.contained']) {
|
|
420
|
+
function eachAssoc( elt, _memberName, _prop, path ) {
|
|
421
|
+
if (elt.target && elt['@odata.contained']) {
|
|
414
422
|
// store all containment associations, required to create the containment paths later on
|
|
415
423
|
container.$containeeAssociations.push( { assoc: elt, path });
|
|
416
424
|
// Let the containee know its container
|
|
417
425
|
// (array because the contanee may contained more then once)
|
|
418
|
-
|
|
426
|
+
const containee = elt._target;
|
|
419
427
|
if (!containee.$containerNames)
|
|
420
428
|
setProp(containee, '$containerNames', []);
|
|
421
429
|
// add container only once per containee
|
|
@@ -423,39 +431,38 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
423
431
|
containee.$containerNames.push(container.name);
|
|
424
432
|
// Mark associations in the containee pointing to the container (i.e. to this entity)
|
|
425
433
|
forEachMemberRecursively(containee, markToContainer,
|
|
426
|
-
|
|
434
|
+
[ 'definitions', containee.name ], true, { elementsOnly: true });
|
|
427
435
|
}
|
|
428
|
-
else if(elt.type && !elt.elements) {
|
|
436
|
+
else if (elt.type && !elt.elements) {
|
|
429
437
|
// try to find elements to drill down further
|
|
430
|
-
while(elt && !isBuiltinType(elt.type) && !elt.elements)
|
|
438
|
+
while (elt && !isBuiltinType(elt.type) && !elt.elements)
|
|
431
439
|
elt = csn.definitions[elt.type];
|
|
432
|
-
|
|
433
|
-
if(elt && elt.elements && !elt.$visited) {
|
|
440
|
+
|
|
441
|
+
if (elt && elt.elements && !elt.$visited) {
|
|
434
442
|
setProp(elt, '$visited', true);
|
|
435
|
-
forEachMemberRecursively(elt,
|
|
436
|
-
|
|
443
|
+
forEachMemberRecursively(elt, eachAssoc,
|
|
444
|
+
path, true, { pathWithoutProp: true, elementsOnly: true });
|
|
437
445
|
delete elt.$visited;
|
|
438
446
|
}
|
|
439
447
|
}
|
|
440
448
|
}
|
|
441
449
|
|
|
442
|
-
function markToContainer(elt) {
|
|
443
|
-
if(elt._target && elt._target.name) {
|
|
450
|
+
function markToContainer( elt ) {
|
|
451
|
+
if (elt._target && elt._target.name) {
|
|
444
452
|
// If this is an association that points to the container (but is not by itself contained,
|
|
445
453
|
// which would indicate the top role in a hierarchy) mark it with '_isToContainer'
|
|
446
|
-
if(elt._target.name === container.name && !elt['odata.contained'])
|
|
454
|
+
if (elt._target.name === container.name && !elt['odata.contained'])
|
|
447
455
|
setProp(elt, '_isToContainer', true);
|
|
448
|
-
}
|
|
449
456
|
}
|
|
450
457
|
else {
|
|
451
458
|
// try to find elements to drill down further
|
|
452
|
-
while(elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
459
|
+
while (elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
453
460
|
elt = csn.definitions[elt.type];
|
|
454
|
-
|
|
455
|
-
if(elt && elt.elements && !elt.$visited) {
|
|
461
|
+
|
|
462
|
+
if (elt && elt.elements && !elt.$visited) {
|
|
456
463
|
setProp(elt, '$visited', true);
|
|
457
464
|
forEachMemberRecursively(elt, markToContainer,
|
|
458
|
-
|
|
465
|
+
[], true, { elementsOnly: true });
|
|
459
466
|
delete elt.$visited;
|
|
460
467
|
}
|
|
461
468
|
}
|
|
@@ -470,9 +477,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
470
477
|
// containment data structures must be manually added here.
|
|
471
478
|
// As a param entity is a potential proxy candidate, this split must be performed on
|
|
472
479
|
// all definitions
|
|
473
|
-
function initParameterizedEntityOrView(entityCsn, entityName) {
|
|
474
|
-
|
|
475
|
-
if(!edmUtils.isParameterizedEntity(entityCsn))
|
|
480
|
+
function initParameterizedEntityOrView( entityCsn, entityName ) {
|
|
481
|
+
if (!edmUtils.isParameterizedEntity(entityCsn))
|
|
476
482
|
return;
|
|
477
483
|
|
|
478
484
|
// Naming rules for aggregated views with parameters
|
|
@@ -487,10 +493,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
487
493
|
// Backlink Navigation Property "Parameters" to <ViewName>Parameters
|
|
488
494
|
|
|
489
495
|
// this code can be extended for aggregated views
|
|
490
|
-
const typeEntityName = entityName
|
|
491
|
-
const typeEntitySetName = entityName
|
|
496
|
+
const typeEntityName = `${entityName}Type`;
|
|
497
|
+
const typeEntitySetName = `${entityName}Set`;
|
|
492
498
|
const typeToParameterAssocName = 'Parameters';
|
|
493
|
-
|
|
499
|
+
const hasBacklink = true;
|
|
494
500
|
|
|
495
501
|
|
|
496
502
|
// create the Parameter Definition
|
|
@@ -498,8 +504,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
498
504
|
setProp(parameterCsn, '_origin', entityCsn);
|
|
499
505
|
// create the Type Definition
|
|
500
506
|
// modify the original parameter entity with backlink and new name
|
|
501
|
-
if(csn.definitions[typeEntityName])
|
|
502
|
-
error('odata-definition-exists', [ 'definitions', entityName ], {
|
|
507
|
+
if (csn.definitions[typeEntityName]) {
|
|
508
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: typeEntityName });
|
|
509
|
+
}
|
|
503
510
|
else {
|
|
504
511
|
csn.definitions[typeEntityName] = entityCsn;
|
|
505
512
|
reqDefs.definitions[typeEntityName] = entityCsn;
|
|
@@ -509,36 +516,38 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
509
516
|
}
|
|
510
517
|
setProp(entityCsn, '$entitySetName', typeEntitySetName);
|
|
511
518
|
// add backlink association
|
|
512
|
-
if(hasBacklink) {
|
|
519
|
+
if (hasBacklink) {
|
|
513
520
|
entityCsn.elements[typeToParameterAssocName] = {
|
|
514
521
|
name: typeToParameterAssocName,
|
|
515
522
|
target: parameterCsn.name,
|
|
516
523
|
type: 'cds.Association',
|
|
517
|
-
on: [ { ref: [ 'Parameters', 'Set' ] }, '=', { ref: [ '$self' ] } ]
|
|
524
|
+
on: [ { ref: [ 'Parameters', 'Set' ] }, '=', { ref: [ '$self' ] } ],
|
|
518
525
|
};
|
|
519
526
|
setProp(entityCsn.elements[typeToParameterAssocName], '_selfReferences', []);
|
|
520
527
|
setProp(entityCsn.elements[typeToParameterAssocName], '_target', parameterCsn);
|
|
521
528
|
setProp(entityCsn.elements[typeToParameterAssocName], '$path',
|
|
522
|
-
|
|
529
|
+
[ 'definitions', typeEntityName, 'elements', typeToParameterAssocName ] );
|
|
523
530
|
|
|
524
531
|
// rewrite $path
|
|
525
|
-
if(entityCsn.$path)
|
|
532
|
+
if (entityCsn.$path)
|
|
526
533
|
entityCsn.$path[1] = typeEntityName;
|
|
527
534
|
forEachMemberRecursively(entityCsn, (member) => {
|
|
528
|
-
if(member.$path)
|
|
535
|
+
if (member.$path)
|
|
529
536
|
member.$path[1] = typeEntityName;
|
|
530
537
|
});
|
|
531
538
|
}
|
|
532
539
|
|
|
533
|
-
/*
|
|
540
|
+
/*
|
|
534
541
|
<EntitySet Name="ZRHA_TEST_CDSSet" EntityType="ZRHA_TEST_CDS_CDS.ZRHA_TEST_CDSType" sap:creatable="false" sap:updatable="false"
|
|
535
542
|
sap:deletable="false" sap:addressable="false" sap:content-version="1"/>
|
|
536
543
|
*/
|
|
537
544
|
edmUtils.assignProp(entityCsn, '_SetAttributes',
|
|
538
|
-
|
|
545
|
+
{
|
|
546
|
+
'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.addressable': false,
|
|
547
|
+
});
|
|
539
548
|
|
|
540
549
|
// redirect inbound associations/compositions to the parameter entity
|
|
541
|
-
Object.keys(entityCsn.$sources || {}).forEach(n => {
|
|
550
|
+
Object.keys(entityCsn.$sources || {}).forEach((n) => {
|
|
542
551
|
// preserve the original target for constraint calculation
|
|
543
552
|
setProp(entityCsn.$sources[n], '_originalTarget', entityCsn.$sources[n]._target);
|
|
544
553
|
entityCsn.$sources[n]._target = parameterCsn;
|
|
@@ -546,8 +555,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
546
555
|
});
|
|
547
556
|
}
|
|
548
557
|
|
|
549
|
-
function createParameterEntity(entityCsn, entityName, isProxy) {
|
|
550
|
-
const parameterEntityName = entityName
|
|
558
|
+
function createParameterEntity( entityCsn, entityName, isProxy ) {
|
|
559
|
+
const parameterEntityName = `${entityName}Parameters`;
|
|
551
560
|
const parameterToTypeAssocName = 'Set';
|
|
552
561
|
|
|
553
562
|
// Construct the parameter entity
|
|
@@ -557,11 +566,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
557
566
|
elements: Object.create(null),
|
|
558
567
|
'@sap.semantics': 'parameters',
|
|
559
568
|
};
|
|
560
|
-
if(!isProxy)
|
|
569
|
+
if (!isProxy)
|
|
561
570
|
setProp(parameterCsn, '$entitySetName', entityName);
|
|
562
|
-
if(entityCsn.$location)
|
|
571
|
+
if (entityCsn.$location)
|
|
563
572
|
edmUtils.assignProp(parameterCsn, '$location', entityCsn.$location);
|
|
564
|
-
|
|
573
|
+
|
|
565
574
|
|
|
566
575
|
/*
|
|
567
576
|
<EntitySet Name="ZRHA_TEST_CDS" EntityType="ZRHA_TEST_CDS_CDS.ZRHA_TEST_CDSParameters" sap:creatable="false" sap:updatable="false"
|
|
@@ -569,13 +578,15 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
569
578
|
*/
|
|
570
579
|
|
|
571
580
|
edmUtils.assignProp(parameterCsn, '_SetAttributes',
|
|
572
|
-
|
|
581
|
+
{
|
|
582
|
+
'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.pageable': false,
|
|
583
|
+
});
|
|
573
584
|
|
|
574
585
|
setProp(parameterCsn, '$isParamEntity', true);
|
|
575
586
|
setProp(parameterCsn, '$mySchemaName', entityCsn.$mySchemaName);
|
|
576
587
|
|
|
577
|
-
forEachGeneric(entityCsn, 'params', (p,n) => {
|
|
578
|
-
|
|
588
|
+
forEachGeneric(entityCsn, 'params', (p, n) => {
|
|
589
|
+
const elt = cloneCsnNonDict(p, options);
|
|
579
590
|
elt.name = n;
|
|
580
591
|
delete elt.kind;
|
|
581
592
|
setProp(elt, '$path', [ 'definitions', parameterEntityName, 'elements', n ]);
|
|
@@ -587,7 +598,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
587
598
|
Only "mandatory" is allowed because in RAP all parameters are NOT NULL
|
|
588
599
|
and so they are in CAP (all view parameters become primary keys which are not null).
|
|
589
600
|
*/
|
|
590
|
-
if(options.isV2())
|
|
601
|
+
if (options.isV2())
|
|
591
602
|
edmUtils.assignAnnotation(elt, '@sap.parameter', 'mandatory');
|
|
592
603
|
else
|
|
593
604
|
edmUtils.assignAnnotation(elt, '@Common.FieldControl', { '#': 'Mandatory' });
|
|
@@ -596,50 +607,52 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
596
607
|
linkAssociationTarget(parameterCsn);
|
|
597
608
|
initContainments(parameterCsn);
|
|
598
609
|
// add assoc to result set, FIXME: is the cardinality correct?
|
|
599
|
-
if(!isProxy) {
|
|
610
|
+
if (!isProxy) {
|
|
600
611
|
parameterCsn.elements[parameterToTypeAssocName] = {
|
|
601
612
|
'@odata.contained': true,
|
|
602
613
|
name: parameterToTypeAssocName,
|
|
603
614
|
target: entityCsn.name,
|
|
604
615
|
type: 'cds.Association',
|
|
605
|
-
cardinality: { src: 1, min: 0, max: '*' }
|
|
616
|
+
cardinality: { src: 1, min: 0, max: '*' },
|
|
606
617
|
};
|
|
607
618
|
setProp(parameterCsn.elements[parameterToTypeAssocName], '_target', entityCsn);
|
|
608
619
|
setProp(parameterCsn.elements[parameterToTypeAssocName], '$path',
|
|
609
|
-
|
|
620
|
+
[ 'definitions', parameterEntityName, 'elements', parameterToTypeAssocName ] );
|
|
610
621
|
}
|
|
611
622
|
|
|
612
|
-
[ '@odata.singleton', '@odata.singleton.nullable' ].forEach(a => {
|
|
613
|
-
if(entityCsn[a] != null)
|
|
623
|
+
[ '@odata.singleton', '@odata.singleton.nullable' ].forEach((a) => {
|
|
624
|
+
if (entityCsn[a] != null)
|
|
614
625
|
parameterCsn[a] = entityCsn[a];
|
|
615
626
|
delete entityCsn[a];
|
|
616
627
|
});
|
|
617
628
|
|
|
618
629
|
// initialize containment
|
|
619
630
|
// propagate containment information, if containment is recursive, use parameterCsn.name as $containerNames
|
|
620
|
-
if(entityCsn.$containerNames) {
|
|
621
|
-
if(!parameterCsn.$containerNames)
|
|
631
|
+
if (entityCsn.$containerNames) {
|
|
632
|
+
if (!parameterCsn.$containerNames)
|
|
622
633
|
setProp(parameterCsn, '$containerNames', []);
|
|
623
|
-
for(const c of entityCsn.$containerNames)
|
|
634
|
+
for (const c of entityCsn.$containerNames)
|
|
624
635
|
parameterCsn.$containerNames.push((c === entityCsn.name) ? parameterCsn.name : c);
|
|
625
|
-
}
|
|
626
636
|
}
|
|
627
637
|
entityCsn.$containerNames = [ parameterCsn ];
|
|
628
638
|
|
|
629
|
-
if(!parameterCsn.$containeeAssociations)
|
|
639
|
+
if (!parameterCsn.$containeeAssociations)
|
|
630
640
|
setProp(parameterCsn, '$containeeAssociations', [ ]);
|
|
631
641
|
parameterCsn.$containeeAssociations.push(
|
|
632
|
-
{
|
|
633
|
-
|
|
634
|
-
|
|
642
|
+
{
|
|
643
|
+
assoc: parameterCsn.elements[parameterToTypeAssocName],
|
|
644
|
+
path: [ parameterToTypeAssocName ],
|
|
645
|
+
}
|
|
646
|
+
);
|
|
635
647
|
|
|
636
648
|
// rewrite $path
|
|
637
649
|
setProp(parameterCsn, '$path', [ 'definitions', parameterEntityName ]);
|
|
638
650
|
|
|
639
651
|
// proxies are registered in model separately
|
|
640
|
-
if(!isProxy) {
|
|
641
|
-
if(csn.definitions[parameterCsn.name])
|
|
652
|
+
if (!isProxy) {
|
|
653
|
+
if (csn.definitions[parameterCsn.name]) {
|
|
642
654
|
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
|
|
655
|
+
}
|
|
643
656
|
else {
|
|
644
657
|
csn.definitions[parameterCsn.name] = parameterCsn;
|
|
645
658
|
reqDefs.definitions[parameterCsn.name] = parameterCsn;
|
|
@@ -648,13 +661,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
648
661
|
return parameterCsn;
|
|
649
662
|
}
|
|
650
663
|
|
|
651
|
-
function initElement(element, name, struct) {
|
|
652
|
-
setProp(element, 'name', name)
|
|
664
|
+
function initElement( element, name, struct ) {
|
|
665
|
+
setProp(element, 'name', name);
|
|
653
666
|
setProp(element, '_parent', struct);
|
|
654
667
|
}
|
|
655
668
|
|
|
656
669
|
// convert $path to path starting at main artifact
|
|
657
|
-
function $path2path(p) {
|
|
670
|
+
function $path2path( p ) {
|
|
658
671
|
const path = [];
|
|
659
672
|
/** @type {any} */
|
|
660
673
|
let env = csn;
|
|
@@ -663,9 +676,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
663
676
|
env = env[ps];
|
|
664
677
|
if (env && env.constructor === Object) {
|
|
665
678
|
path.push(ps);
|
|
666
|
-
if
|
|
679
|
+
// jump over many items but not if this is an element
|
|
680
|
+
if (env.items) {
|
|
667
681
|
env = env.items;
|
|
668
|
-
|
|
682
|
+
if (p[i + 1] === 'items')
|
|
683
|
+
i++;
|
|
684
|
+
}
|
|
685
|
+
if (env.type && !isBuiltinType(env.type) && !env.elements)
|
|
669
686
|
env = csn.definitions[env.type];
|
|
670
687
|
}
|
|
671
688
|
}
|
|
@@ -673,45 +690,49 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
673
690
|
}
|
|
674
691
|
|
|
675
692
|
// Initialize a structured artifact
|
|
676
|
-
function initStructure(def) {
|
|
677
|
-
|
|
693
|
+
function initStructure( def ) {
|
|
678
694
|
// Don't operate on any structured types other than type and entity
|
|
679
695
|
// such as events and aspects
|
|
680
|
-
if(!edmUtils.isStructuredArtifact(def))
|
|
696
|
+
if (!edmUtils.isStructuredArtifact(def))
|
|
681
697
|
return;
|
|
682
698
|
|
|
683
699
|
let keys = Object.create(null);
|
|
684
|
-
|
|
700
|
+
const validFrom = []; const
|
|
701
|
+
validKey = [];
|
|
685
702
|
|
|
686
703
|
// Iterate all struct elements
|
|
687
704
|
forEachMemberRecursively(def.items || def, (element, elementName, prop, _path, construct) => {
|
|
688
|
-
if(prop !== 'elements')
|
|
705
|
+
if (prop !== 'elements')
|
|
689
706
|
return;
|
|
690
707
|
|
|
691
708
|
initElement(element, elementName, construct);
|
|
692
709
|
|
|
693
710
|
// collect temporal information
|
|
694
|
-
if(element['@cds.valid.key'])
|
|
711
|
+
if (element['@cds.valid.key'])
|
|
695
712
|
validKey.push(element);
|
|
696
|
-
|
|
697
|
-
if(element['@cds.valid.from'])
|
|
713
|
+
|
|
714
|
+
if (element['@cds.valid.from'])
|
|
698
715
|
validFrom.push(element);
|
|
699
|
-
|
|
700
|
-
//forward annotations from managed association element to its foreign keys
|
|
716
|
+
|
|
717
|
+
// forward annotations from managed association element to its foreign keys
|
|
718
|
+
// TODO: Try to eliminate this code by rearranging
|
|
719
|
+
// forOdata::addCommonValueListviaAssociation(member, memberName)
|
|
720
|
+
// such that the VL annotations are distributed to the associations *before*
|
|
721
|
+
// FK creation.
|
|
722
|
+
// The FK creation already propagates the annotations from the association
|
|
701
723
|
const elements = construct.items && construct.items.elements || construct.elements;
|
|
702
724
|
const fk = elements[element['@odata.foreignKey4']];
|
|
703
|
-
for(const attrName in fk) {
|
|
725
|
+
for (const attrName in fk) {
|
|
704
726
|
const attr = fk[attrName];
|
|
705
|
-
if(attrName[0] === '@')
|
|
706
|
-
element
|
|
707
|
-
}
|
|
727
|
+
if (attrName[0] === '@')
|
|
728
|
+
edmUtils.assignAnnotation(element, attrName, attr);
|
|
708
729
|
}
|
|
709
730
|
// and eventually remove some afterwards
|
|
710
|
-
if(options.isV2())
|
|
731
|
+
if (options.isV2())
|
|
711
732
|
edmAnnoPreproc.setSAPSpecificV2AnnotationsToAssociation(element);
|
|
712
733
|
|
|
713
734
|
// initialize an association
|
|
714
|
-
if(element.target) {
|
|
735
|
+
if (element.target) {
|
|
715
736
|
// in case this is a forward assoc, store the backlink partners here, _selfReferences.length > 1 => error
|
|
716
737
|
edmUtils.assignProp(element, '_selfReferences', []);
|
|
717
738
|
edmUtils.assignProp(element._target, '$proxies', []);
|
|
@@ -720,50 +741,48 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
720
741
|
}
|
|
721
742
|
|
|
722
743
|
// Collect keys
|
|
723
|
-
if (element.key)
|
|
744
|
+
if (element.key)
|
|
724
745
|
keys[elementName] = element;
|
|
725
|
-
|
|
746
|
+
|
|
726
747
|
edmAnnoPreproc.applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
|
|
727
748
|
}, [], true, { elementsOnly: true });
|
|
728
749
|
|
|
729
|
-
if(!isDeprecatedEnabled(options, '_v1KeysForTemporal')) {
|
|
750
|
+
if (!isDeprecatedEnabled(options, '_v1KeysForTemporal')) {
|
|
730
751
|
// if artifact has a cds.valid.key mention it as @Core.AlternateKey
|
|
731
|
-
if(validKey.length) {
|
|
732
|
-
|
|
752
|
+
if (validKey.length) {
|
|
753
|
+
const altKeys = [ { Key: [] } ];
|
|
733
754
|
validKey.forEach(vk => altKeys[0].Key.push( { Name: vk.name, Alias: vk.name } ) );
|
|
734
755
|
edmUtils.assignAnnotation(def, '@Core.AlternateKeys', altKeys);
|
|
735
756
|
}
|
|
736
757
|
}
|
|
737
|
-
else {
|
|
758
|
+
else if (validKey.length) {
|
|
738
759
|
// if artifact has a cds.valid.key make this the only primary key and
|
|
739
760
|
// add all @cds.valid.from + original primary keys as alternate keys
|
|
740
761
|
// @Core.AlternateKeys: [{ Key: [ { Name: 'slID', Alias: 'slID' }, { Name: 'validFrom', Alias: 'validFrom'} ] }]
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
});
|
|
762
|
-
}
|
|
762
|
+
const altKeys = [ { Key: [] } ];
|
|
763
|
+
Object.entries(([ kn, k ]) => {
|
|
764
|
+
altKeys[0].Key.push( { Name: kn, Alias: kn } );
|
|
765
|
+
delete k.key;
|
|
766
|
+
});
|
|
767
|
+
validFrom.forEach((e) => {
|
|
768
|
+
altKeys[0].Key.push( { Name: e.name, Alias: e.name } );
|
|
769
|
+
});
|
|
770
|
+
edmUtils.assignAnnotation(def, '@Core.AlternateKeys', altKeys);
|
|
771
|
+
keys = Object.create(null);
|
|
772
|
+
validKey.forEach((e) => {
|
|
773
|
+
e.key = true;
|
|
774
|
+
keys[e.name] = e;
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
validFrom.forEach((e) => {
|
|
779
|
+
e.key = true;
|
|
780
|
+
keys[e.name] = e;
|
|
781
|
+
});
|
|
763
782
|
}
|
|
764
783
|
|
|
765
784
|
// prepare the structure itself
|
|
766
|
-
if(def.kind === 'entity') {
|
|
785
|
+
if (def.kind === 'entity') {
|
|
767
786
|
edmUtils.assignProp(def, '_SetAttributes', Object.create(null));
|
|
768
787
|
edmUtils.assignProp(def, '$keys', keys);
|
|
769
788
|
edmAnnoPreproc.applyAppSpecificLateCsnTransformationOnStructure(options, def, error);
|
|
@@ -772,17 +791,19 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
772
791
|
}
|
|
773
792
|
|
|
774
793
|
// Prepare the associations for the subsequent steps
|
|
775
|
-
function initConstraints(def) {
|
|
776
|
-
if(!edmUtils.isStructuredArtifact(def))
|
|
794
|
+
function initConstraints( def ) {
|
|
795
|
+
if (!edmUtils.isStructuredArtifact(def))
|
|
777
796
|
return;
|
|
778
797
|
|
|
779
798
|
forEachMemberRecursively(def.items || def, initConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
780
799
|
}
|
|
781
|
-
function initConstraintsOnAssoc(element) {
|
|
800
|
+
function initConstraintsOnAssoc( element ) {
|
|
782
801
|
if (element.target && !element._constraints) {
|
|
783
|
-
|
|
784
|
-
setProp(element, '_constraints', {
|
|
785
|
-
|
|
802
|
+
// setup the constraints object
|
|
803
|
+
setProp(element, '_constraints', {
|
|
804
|
+
constraints: Object.create(null), selfs: [], _origins: [], termCount: 0,
|
|
805
|
+
});
|
|
806
|
+
// and crack the ON condition
|
|
786
807
|
edmUtils.resolveOnConditionAndPrepareConstraints(csn, element, messageFunctions);
|
|
787
808
|
}
|
|
788
809
|
}
|
|
@@ -799,53 +820,52 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
799
820
|
active, assign @cds.api.ignore or @odata.navigable: false
|
|
800
821
|
4) All of this can be revoked with options.renderForeignKeys.
|
|
801
822
|
*/
|
|
802
|
-
function ignoreProperties(struct) {
|
|
803
|
-
if(!edmUtils.isStructuredArtifact(struct))
|
|
823
|
+
function ignoreProperties( struct ) {
|
|
824
|
+
if (!edmUtils.isStructuredArtifact(struct))
|
|
804
825
|
return;
|
|
805
826
|
|
|
806
827
|
forEachMemberRecursively(struct.items || struct, (element) => {
|
|
807
|
-
if(!element.target) {
|
|
808
|
-
if(element['@odata.foreignKey4']) {
|
|
828
|
+
if (!element.target) {
|
|
829
|
+
if (element['@odata.foreignKey4']) {
|
|
809
830
|
let isContainerAssoc = false;
|
|
810
|
-
let elements =
|
|
811
|
-
let assoc
|
|
812
|
-
const paths = element['@odata.foreignKey4'].split('.')
|
|
813
|
-
for(
|
|
831
|
+
let { elements } = struct.items || struct;
|
|
832
|
+
let assoc;
|
|
833
|
+
const paths = element['@odata.foreignKey4'].split('.');
|
|
834
|
+
for (const p of paths) {
|
|
814
835
|
assoc = elements[p];
|
|
815
|
-
if(assoc) // could be that the @odata.foreignKey4 was propagated...
|
|
836
|
+
if (assoc) // could be that the @odata.foreignKey4 was propagated...
|
|
816
837
|
elements = assoc.elements;
|
|
817
838
|
}
|
|
818
839
|
|
|
819
|
-
if(assoc)
|
|
840
|
+
if (assoc)
|
|
820
841
|
isContainerAssoc = !!(assoc._isToContainer && assoc._selfReferences.length || assoc['@odata.contained']);
|
|
821
842
|
/*
|
|
822
843
|
If this foreign key is NOT a container fk, let isEdmPropertyRendered() decide
|
|
823
844
|
Else, if fk is container fk, omit it if it wasn't requested in structured mode
|
|
824
845
|
*/
|
|
825
|
-
if((!isContainerAssoc && !isEdmPropertyRendered(element, options)) ||
|
|
846
|
+
if ((!isContainerAssoc && !isEdmPropertyRendered(element, options)) ||
|
|
826
847
|
(isContainerAssoc && !options.renderForeignKeys))
|
|
827
848
|
edmUtils.assignAnnotation(element, '@cds.api.ignore', true);
|
|
828
849
|
// Only in containment:
|
|
829
850
|
// If this element is a foreign key and if it is rendered, remove it from the key ref vector (if available)
|
|
830
|
-
else if(options.odataContainment &&
|
|
851
|
+
else if (options.odataContainment &&
|
|
831
852
|
isContainerAssoc &&
|
|
832
853
|
options.renderForeignKeys &&
|
|
833
|
-
struct.$keys)
|
|
854
|
+
struct.$keys)
|
|
834
855
|
delete struct.$keys[element.name];
|
|
835
|
-
}
|
|
836
856
|
}
|
|
837
857
|
// Only in containment:
|
|
838
858
|
// Ignore this (foreign key) element if renderForeignKeys is false
|
|
839
|
-
if(options.odataContainment && element['@odata.containment.ignore']) {
|
|
840
|
-
if(!options.renderForeignKeys)
|
|
859
|
+
if (options.odataContainment && element['@odata.containment.ignore']) {
|
|
860
|
+
if (!options.renderForeignKeys)
|
|
841
861
|
edmUtils.assignAnnotation(element, '@cds.api.ignore', true);
|
|
842
|
-
else if(struct.$keys)
|
|
862
|
+
else if (struct.$keys)
|
|
843
863
|
// If foreign keys shall be rendered, remove it from key ref vector (if available)
|
|
844
864
|
delete struct.$keys[element.name];
|
|
845
865
|
}
|
|
846
866
|
}
|
|
847
|
-
|
|
848
|
-
else if(element['@odata.containment.ignore'] && options.odataContainment && !options.renderForeignKeys) {
|
|
867
|
+
// it's an association
|
|
868
|
+
else if (element['@odata.containment.ignore'] && options.odataContainment && !options.renderForeignKeys) {
|
|
849
869
|
// if this is an explicitly containment ignore tagged association,
|
|
850
870
|
// ignore it if option odataContainment is true and no foreign keys should be rendered
|
|
851
871
|
edmUtils.assignAnnotation(element, '@odata.navigable', false);
|
|
@@ -858,43 +878,44 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
858
878
|
It may be that now a number of properties are not rendered and cannot act as constraints (see isConstraintCandidate())
|
|
859
879
|
in edmUtils
|
|
860
880
|
*/
|
|
861
|
-
function finalizeConstraints(def) {
|
|
862
|
-
if(!edmUtils.isStructuredArtifact(def))
|
|
881
|
+
function finalizeConstraints( def ) {
|
|
882
|
+
if (!edmUtils.isStructuredArtifact(def))
|
|
863
883
|
return;
|
|
864
884
|
|
|
865
885
|
forEachMemberRecursively(def.items || def, finalizeConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
866
886
|
}
|
|
867
|
-
function finalizeConstraintsOnAssoc(element) {
|
|
887
|
+
function finalizeConstraintsOnAssoc( element ) {
|
|
868
888
|
if (element.target && element._constraints) {
|
|
869
889
|
edmUtils.finalizeReferentialConstraints(csn, element, options, info);
|
|
870
890
|
|
|
871
|
-
if(element._constraints?._partnerCsn) {
|
|
891
|
+
if (element._constraints?._partnerCsn) {
|
|
872
892
|
// if this is a partnership and this assoc has a set target cardinality, assign it as source cardinality to the partner
|
|
873
|
-
if(element._constraints._partnerCsn.cardinality) {
|
|
893
|
+
if (element._constraints._partnerCsn.cardinality) {
|
|
874
894
|
// if the forward association has set a src cardinality and it deviates from the backlink target cardinality raise a warning
|
|
875
895
|
// in V2 only, in V4 the source cardinality is rendered implicitly at the Type property
|
|
876
|
-
if(element._constraints._partnerCsn.cardinality.src) {
|
|
896
|
+
if (element._constraints._partnerCsn.cardinality.src) {
|
|
897
|
+
// eslint-disable-next-line eqeqeq
|
|
877
898
|
const srcMult = (element._constraints._partnerCsn.cardinality.src == 1) ? '0..1' : '*';
|
|
878
|
-
const newMult
|
|
879
|
-
(element.cardinality?.min == 1 && element.cardinality?.max == 1)
|
|
899
|
+
const newMult
|
|
900
|
+
= (element.cardinality?.min == 1 && element.cardinality?.max == 1) // eslint-disable-line eqeqeq
|
|
901
|
+
|
|
880
902
|
? '1'
|
|
881
903
|
: (element.cardinality?.max === '*' || element.cardinality?.max > 1)
|
|
882
904
|
? '*'
|
|
883
905
|
: '0..1';
|
|
884
|
-
if(srcMult !== newMult)
|
|
906
|
+
if (srcMult !== newMult)
|
|
885
907
|
warning(null, element.$path, `Explicit source cardinality "${srcMult}" of "${element._constraints._partnerCsn._parent.name}/${element._constraints._partnerCsn.name}" conflicts with target cardinality "${newMult}"`);
|
|
886
|
-
}
|
|
887
908
|
}
|
|
888
909
|
else {
|
|
889
910
|
// .. but only if the original assoc hasn't set src yet
|
|
890
911
|
element._constraints._partnerCsn.cardinality.src = element.cardinality?.max ? element.cardinality.max : 1;
|
|
891
|
-
if(element.cardinality?.min !== undefined && element._constraints._partnerCsn.cardinality?.srcmin === undefined)
|
|
912
|
+
if (element.cardinality?.min !== undefined && element._constraints._partnerCsn.cardinality?.srcmin === undefined)
|
|
892
913
|
element._constraints._partnerCsn.cardinality.srcmin = element.cardinality.min;
|
|
893
914
|
}
|
|
894
915
|
}
|
|
895
916
|
else {
|
|
896
917
|
element._constraints._partnerCsn.cardinality = { src: element.cardinality?.max ? element.cardinality.max : 1 };
|
|
897
|
-
if(element.cardinality?.min !== undefined)
|
|
918
|
+
if (element.cardinality?.min !== undefined)
|
|
898
919
|
element._constraints._partnerCsn.cardinality.srcmin = element.cardinality.min;
|
|
899
920
|
}
|
|
900
921
|
}
|
|
@@ -907,20 +928,20 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
907
928
|
sub artifacts exposed by the initial type exposure
|
|
908
929
|
*/
|
|
909
930
|
function convertExposedTypesOfOtherServicesIntoCrossReferences() {
|
|
910
|
-
if(options.odataXServiceRefs && options.isV4()) {
|
|
911
|
-
serviceRootNames.forEach(srn => {
|
|
912
|
-
schemaNames.forEach(fqSchemaName => {
|
|
913
|
-
if(fqSchemaName.startsWith(srn
|
|
914
|
-
const targetSchemaName = fqSchemaName.replace(srn
|
|
915
|
-
if(serviceRootNames.includes(targetSchemaName)) {
|
|
931
|
+
if (options.odataXServiceRefs && options.isV4()) {
|
|
932
|
+
serviceRootNames.forEach((srn) => {
|
|
933
|
+
schemaNames.forEach((fqSchemaName) => {
|
|
934
|
+
if (fqSchemaName.startsWith(`${srn}.`)) {
|
|
935
|
+
const targetSchemaName = fqSchemaName.replace(`${srn}.`, '');
|
|
936
|
+
if (serviceRootNames.includes(targetSchemaName)) {
|
|
916
937
|
// remove all definitions starting with < fqSchemaName >. and add a schema reference
|
|
917
|
-
Object.keys(csn.definitions).forEach(dn => {
|
|
918
|
-
if(dn.startsWith(fqSchemaName)) {
|
|
938
|
+
Object.keys(csn.definitions).forEach((dn) => {
|
|
939
|
+
if (dn.startsWith(fqSchemaName)) {
|
|
919
940
|
delete csn.definitions[dn];
|
|
920
941
|
delete reqDefs.definitions[dn];
|
|
921
942
|
}
|
|
922
943
|
});
|
|
923
|
-
if(!schemas[fqSchemaName])
|
|
944
|
+
if (!schemas[fqSchemaName])
|
|
924
945
|
schemaNames.push(fqSchemaName);
|
|
925
946
|
schemas[fqSchemaName] = edmUtils.createSchemaRef(serviceRoots, targetSchemaName);
|
|
926
947
|
}
|
|
@@ -928,7 +949,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
928
949
|
});
|
|
929
950
|
});
|
|
930
951
|
}
|
|
931
|
-
schemaNames.sort((a,b)=>b.length-a.length);
|
|
952
|
+
schemaNames.sort((a, b) => b.length - a.length);
|
|
932
953
|
}
|
|
933
954
|
|
|
934
955
|
/*
|
|
@@ -953,8 +974,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
953
974
|
or indirectly (via structured elements), these dependent entity types are (recursively) exposed
|
|
954
975
|
(or referenced) as well to keep the navigation graph in tact.
|
|
955
976
|
*/
|
|
956
|
-
function exposeTargetsAsProxiesOrSchemaRefs(struct) {
|
|
957
|
-
if(struct.kind === 'context' || struct.kind === 'service' || struct.$proxy)
|
|
977
|
+
function exposeTargetsAsProxiesOrSchemaRefs( struct ) {
|
|
978
|
+
if (struct.kind === 'context' || struct.kind === 'service' || struct.$proxy)
|
|
958
979
|
return;
|
|
959
980
|
|
|
960
981
|
// globalSchemaPrefix is the prefix for all proxy registrations and must not change
|
|
@@ -962,9 +983,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
962
983
|
// definitions which are directly below the root service ($mySchemaName is the root)
|
|
963
984
|
const globalSchemaPrefix = whatsMyServiceRootName(struct.$mySchemaName);
|
|
964
985
|
// if this artifact is a service member check its associations
|
|
965
|
-
if(globalSchemaPrefix) {
|
|
966
|
-
forEachGeneric(struct.items || struct, 'elements', element => {
|
|
967
|
-
if(!edmUtils.isNavigable(element))
|
|
986
|
+
if (globalSchemaPrefix) {
|
|
987
|
+
forEachGeneric(struct.items || struct, 'elements', (element) => {
|
|
988
|
+
if (!edmUtils.isNavigable(element))
|
|
968
989
|
return;
|
|
969
990
|
/*
|
|
970
991
|
* Consider everything @cds.autoexpose: falsy to be a proxy candidate for now
|
|
@@ -999,36 +1020,37 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
999
1020
|
// 1 | 1 | Cross service references and proxies are generated
|
|
1000
1021
|
|
|
1001
1022
|
const targetSchemaName = element._target.$mySchemaName;
|
|
1002
|
-
if(isProxyRequired(element)) {
|
|
1003
|
-
if(options.isV4() && (options.odataProxies || options.odataXServiceRefs)) {
|
|
1023
|
+
if (isProxyRequired(element)) {
|
|
1024
|
+
if (options.isV4() && (options.odataProxies || options.odataXServiceRefs)) {
|
|
1004
1025
|
// must be a managed association with keys OR an unambiguous backlink to become a proxy
|
|
1005
|
-
|
|
1026
|
+
const assocOK = element.keys ||
|
|
1006
1027
|
(element.on && element._constraints.selfs.length === 1 && element._constraints.termCount === 1);
|
|
1007
1028
|
// reuse proxy if available
|
|
1008
1029
|
let proxy = getProxyForTargetOf(element);
|
|
1009
|
-
if(!proxy) {
|
|
1010
|
-
if(targetSchemaName && options.odataXServiceRefs)
|
|
1030
|
+
if (!proxy) {
|
|
1031
|
+
if (targetSchemaName && options.odataXServiceRefs)
|
|
1011
1032
|
proxy = createSchemaRefFor(targetSchemaName);
|
|
1012
|
-
|
|
1033
|
+
|
|
1013
1034
|
// create a proxy for a 'good' association only
|
|
1014
|
-
else if(options.odataProxies && assocOK)
|
|
1035
|
+
else if (options.odataProxies && assocOK)
|
|
1015
1036
|
proxy = createProxyFor(element, targetSchemaName);
|
|
1016
|
-
|
|
1037
|
+
|
|
1017
1038
|
proxy = registerProxy(proxy, element);
|
|
1018
|
-
}
|
|
1039
|
+
}
|
|
1040
|
+
else if (!assocOK) {
|
|
1019
1041
|
// if there is already a proxy (generated by a 'good' association)
|
|
1020
1042
|
// and this association is not a good one, don't expose this association.
|
|
1021
1043
|
muteNavProp(element, 'oncond');
|
|
1022
1044
|
return;
|
|
1023
1045
|
}
|
|
1024
|
-
if(proxy) {
|
|
1046
|
+
if (proxy) {
|
|
1025
1047
|
// if a proxy was either already created or could be created and
|
|
1026
1048
|
// if it's a 'real' proxy, link the _target to it and remove constraints
|
|
1027
1049
|
// otherwise proxy is a schema reference, then do nothing
|
|
1028
1050
|
setProp(element, '$noPartner', true);
|
|
1029
1051
|
element._constraints.constraints = Object.create(null);
|
|
1030
|
-
if(proxy.kind === 'entity') {
|
|
1031
|
-
if(!proxy.$isParamEntity)
|
|
1052
|
+
if (proxy.kind === 'entity') {
|
|
1053
|
+
if (!proxy.$isParamEntity)
|
|
1032
1054
|
populateProxyElements(element, proxy, getForeignKeyDefinitions(element));
|
|
1033
1055
|
element._target = proxy;
|
|
1034
1056
|
}
|
|
@@ -1050,22 +1072,22 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1050
1072
|
});
|
|
1051
1073
|
}
|
|
1052
1074
|
|
|
1053
|
-
function muteNavProp(elt, msg='std') {
|
|
1075
|
+
function muteNavProp( elt, msg = 'std' ) {
|
|
1054
1076
|
edmUtils.assignAnnotation(elt, '@odata.navigable', false);
|
|
1055
|
-
warning('odata-navigation', ['definitions', struct.name, 'elements', elt.name],
|
|
1056
|
-
|
|
1077
|
+
warning('odata-navigation', [ 'definitions', struct.name, 'elements', elt.name ],
|
|
1078
|
+
{ target: elt._target.name, service: globalSchemaPrefix, '#': msg });
|
|
1057
1079
|
}
|
|
1058
1080
|
|
|
1059
|
-
function createSchemaRefFor(targetSchemaName) {
|
|
1060
|
-
let ref = csn.definitions[globalSchemaPrefix
|
|
1061
|
-
if(!ref)
|
|
1081
|
+
function createSchemaRefFor( targetSchemaName ) {
|
|
1082
|
+
let ref = csn.definitions[`${globalSchemaPrefix}.${targetSchemaName}`];
|
|
1083
|
+
if (!ref)
|
|
1062
1084
|
ref = edmUtils.createSchemaRef(serviceRoots, targetSchemaName);
|
|
1063
|
-
|
|
1085
|
+
|
|
1064
1086
|
|
|
1065
1087
|
return ref;
|
|
1066
1088
|
}
|
|
1067
1089
|
|
|
1068
|
-
function createProxyFor(assoc, targetSchemaName) {
|
|
1090
|
+
function createProxyFor( assoc, targetSchemaName ) {
|
|
1069
1091
|
// If target is outside any service expose it in service of source entity
|
|
1070
1092
|
// The proxySchemaName is not prepended with the service schema name to allow to share the proxy
|
|
1071
1093
|
// if it is required in multiple services. The service schema name is prepended upon registration
|
|
@@ -1076,13 +1098,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1076
1098
|
|
|
1077
1099
|
// 1) construct the proxy definition
|
|
1078
1100
|
// proxyDefinitionName: strip the serviceName and replace '.' with '_'
|
|
1079
|
-
|
|
1080
|
-
`${assoc._target.name.replace(proxySchemaName
|
|
1101
|
+
const defName
|
|
1102
|
+
= `${assoc._target.name.replace(`${proxySchemaName}.`, '').replace(/\./g, '_')}`;
|
|
1081
1103
|
|
|
1082
1104
|
// fullName: Prepend serviceName and if in same service add '_proxy'
|
|
1083
1105
|
const proxy = isParamProxy
|
|
1084
|
-
? createParameterEntity(assoc._target,
|
|
1085
|
-
: { name: proxySchemaName
|
|
1106
|
+
? createParameterEntity(assoc._target, `${proxySchemaName}.${defName}`, true)
|
|
1107
|
+
: { name: `${proxySchemaName}.${defName}`, kind: 'entity', elements: Object.create(null) };
|
|
1086
1108
|
|
|
1087
1109
|
// Final proxyShortName for all further processing
|
|
1088
1110
|
const proxyShortName = defName + (isParamProxy ? 'Parameters' : '');
|
|
@@ -1094,77 +1116,78 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1094
1116
|
setProp(proxy, '$hasEntitySet', false);
|
|
1095
1117
|
setProp(proxy, '$exposedTypes', Object.create(null));
|
|
1096
1118
|
// copy all annotations of the target to the proxy
|
|
1097
|
-
Object.entries(assoc._target).forEach(([k, v]) => {
|
|
1098
|
-
if(k[0] === '@' && k !== '@open')
|
|
1119
|
+
Object.entries(assoc._target).forEach(([ k, v ]) => {
|
|
1120
|
+
if (k[0] === '@' && k !== '@open')
|
|
1099
1121
|
proxy[k] = v;
|
|
1100
1122
|
});
|
|
1101
1123
|
|
|
1102
1124
|
// 2) create the elements and $keys
|
|
1103
|
-
if(isParamProxy) {
|
|
1125
|
+
if (isParamProxy) {
|
|
1104
1126
|
// Reset param proxy elements to expose element tree
|
|
1105
|
-
const elements = proxy
|
|
1127
|
+
const { elements } = proxy;
|
|
1106
1128
|
proxy.elements = Object.create(null);
|
|
1107
1129
|
populateProxyElements(assoc, proxy, elements);
|
|
1108
1130
|
}
|
|
1109
|
-
else
|
|
1131
|
+
else {
|
|
1110
1132
|
populateProxyElements(assoc, proxy, assoc._target.$keys);
|
|
1133
|
+
}
|
|
1111
1134
|
return proxy;
|
|
1112
|
-
|
|
1113
1135
|
}
|
|
1114
1136
|
|
|
1115
1137
|
// Return top level foreign key element definitions. The full top level
|
|
1116
1138
|
// element is exposed instead of merging partial trees into the exposed type
|
|
1117
1139
|
// def structure.
|
|
1118
|
-
function getForeignKeyDefinitions(e) {
|
|
1140
|
+
function getForeignKeyDefinitions( e ) {
|
|
1119
1141
|
return e.keys ? e.keys.map(fk => e._target.elements[fk.ref[0]]) : [];
|
|
1120
1142
|
}
|
|
1121
1143
|
|
|
1122
1144
|
// copy over the primary keys of the target and trigger the type exposure
|
|
1123
1145
|
// if the element already exists we assume it was fully exposed
|
|
1124
|
-
function populateProxyElements(assoc, proxy, elements) {
|
|
1125
|
-
Object.values(elements).forEach(e => {
|
|
1146
|
+
function populateProxyElements( assoc, proxy, elements ) {
|
|
1147
|
+
Object.values(elements).forEach((e) => {
|
|
1126
1148
|
if (isEdmPropertyRendered(e, options)) {
|
|
1127
1149
|
let newElt = proxy.elements[e.name];
|
|
1128
|
-
if(!newElt) {
|
|
1129
|
-
if(csnUtils.isAssocOrComposition(e)) {
|
|
1130
|
-
if(!e.on && e.keys) {
|
|
1131
|
-
if(options.odataNoTransitiveProxies)
|
|
1150
|
+
if (!newElt) {
|
|
1151
|
+
if (csnUtils.isAssocOrComposition(e)) {
|
|
1152
|
+
if (!e.on && e.keys) {
|
|
1153
|
+
if (options.odataNoTransitiveProxies)
|
|
1132
1154
|
newElt = convertManagedAssocIntoStruct(e);
|
|
1133
1155
|
else
|
|
1134
1156
|
newElt = createProxyOrSchemaRefForManagedAssoc(e);
|
|
1135
1157
|
}
|
|
1136
1158
|
else {
|
|
1137
|
-
info(null, ['definitions', struct.name, 'elements', assoc.name],
|
|
1138
|
-
|
|
1139
|
-
|
|
1159
|
+
info(null, [ 'definitions', struct.name, 'elements', assoc.name ],
|
|
1160
|
+
{ name: proxy.nname, target: assoc._target.name },
|
|
1161
|
+
'Unmanaged associations are not supported as primary keys for proxy entity type $(NAME) of unexposed association target $(TARGET)');
|
|
1140
1162
|
}
|
|
1141
1163
|
}
|
|
1142
1164
|
else {
|
|
1143
1165
|
newElt = Object.create(null);
|
|
1144
|
-
Object.keys(e).forEach(prop =>
|
|
1166
|
+
Object.keys(e).forEach((prop) => {
|
|
1167
|
+
newElt[prop] = e[prop];
|
|
1168
|
+
});
|
|
1145
1169
|
}
|
|
1146
|
-
if(newElt) {
|
|
1170
|
+
if (newElt) {
|
|
1147
1171
|
initElement(newElt, e.name, proxy);
|
|
1148
1172
|
proxy.elements[newElt.name] = newElt;
|
|
1149
1173
|
|
|
1150
|
-
if(csnUtils.isStructured(newElt)) {
|
|
1174
|
+
if (csnUtils.isStructured(newElt)) {
|
|
1151
1175
|
// argument proxySchemaName forces an anonymous type definition for newElt into the
|
|
1152
1176
|
// proxy schema. If omitted, this exposure defaults to 'root', in case API flavor
|
|
1153
1177
|
// of the day changes...
|
|
1154
|
-
exposeStructTypeForProxyOf(
|
|
1155
|
-
|
|
1178
|
+
exposeStructTypeForProxyOf(newElt, `${proxy.$proxyShortName}_${newElt.name}`,
|
|
1179
|
+
proxy.$mySchemaName, newElt.key, !!(newElt.key && newElt.elements));
|
|
1156
1180
|
}
|
|
1157
|
-
if(newElt.key)
|
|
1181
|
+
if (newElt.key)
|
|
1158
1182
|
proxy.$keys[newElt.name] = newElt;
|
|
1159
1183
|
}
|
|
1160
1184
|
}
|
|
1161
1185
|
}
|
|
1162
|
-
|
|
1163
1186
|
});
|
|
1164
1187
|
// 3) sort the exposed types so that they appear lexicographically ordered in the EDM
|
|
1165
1188
|
proxy.$exposedTypes = Object.keys(proxy.$exposedTypes).sort().reduce((dict, tn) => {
|
|
1166
1189
|
dict[tn] = proxy.$exposedTypes[tn];
|
|
1167
|
-
return dict
|
|
1190
|
+
return dict;
|
|
1168
1191
|
}, Object.create(null));
|
|
1169
1192
|
|
|
1170
1193
|
// If 'node' exists and has a structured type that is not exposed in 'service', (because the type is
|
|
@@ -1174,11 +1197,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1174
1197
|
// isKey: Indicates top level element is key or not
|
|
1175
1198
|
// forceToNotNull: if top level element is key, recursively set all anonymously exposed elements
|
|
1176
1199
|
// to notNull until the first named type is exposed.
|
|
1177
|
-
function exposeStructTypeForProxyOf(
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
if(node.type && isBuiltinType(node.type))
|
|
1200
|
+
function exposeStructTypeForProxyOf( node, artificialName,
|
|
1201
|
+
typeSchemaName = fallBackSchemaName,
|
|
1202
|
+
isKey = false, forceToNotNull = false ) {
|
|
1203
|
+
if (node.type && isBuiltinType(node.type))
|
|
1182
1204
|
return;
|
|
1183
1205
|
|
|
1184
1206
|
// Always expose types referred to by a proxy, never reuse an eventually existing type
|
|
@@ -1191,43 +1213,46 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1191
1213
|
// the type clone must be produced for each service as this type may
|
|
1192
1214
|
// produce references and/or proxies into multiple services
|
|
1193
1215
|
// (but only once per service, therefore cache it).
|
|
1194
|
-
if(typeDef.$proxyTypes && typeDef.$proxyTypes[globalSchemaPrefix]) {
|
|
1195
|
-
|
|
1216
|
+
if (typeDef.$proxyTypes && typeDef.$proxyTypes[globalSchemaPrefix]) {
|
|
1217
|
+
// if type has been exposed in a schema use this type
|
|
1196
1218
|
typeClone = typeDef.$proxyTypes[globalSchemaPrefix];
|
|
1197
1219
|
}
|
|
1198
1220
|
else {
|
|
1199
|
-
|
|
1221
|
+
// Set the correct name
|
|
1200
1222
|
let typeId = artificialName; // the artificialName has no namespace, it's the element
|
|
1201
|
-
if(node.type) {
|
|
1223
|
+
if (node.type) {
|
|
1202
1224
|
// same as for proxies, use schema or namespace, 'root' is last resort
|
|
1203
1225
|
typeSchemaName = typeDef.$mySchemaName || edmUtils.getSchemaPrefix(node.type);
|
|
1204
|
-
typeId = node.type.replace(typeSchemaName
|
|
1226
|
+
typeId = node.type.replace(`${typeSchemaName}.`, '').replace(/\./g, '_');
|
|
1205
1227
|
// strip the service root of that type (if any)
|
|
1206
1228
|
const myServiceRootName = whatsMyServiceRootName(typeSchemaName);
|
|
1207
|
-
if(myServiceRootName)
|
|
1208
|
-
typeSchemaName = typeSchemaName.replace(myServiceRootName
|
|
1229
|
+
if (myServiceRootName)
|
|
1230
|
+
typeSchemaName = typeSchemaName.replace(`${myServiceRootName}.`, '');
|
|
1209
1231
|
}
|
|
1210
1232
|
|
|
1211
|
-
if(edmUtils.isStructuredArtifact(typeDef)) {
|
|
1233
|
+
if (edmUtils.isStructuredArtifact(typeDef)) {
|
|
1212
1234
|
// pull forceNotNull to false for named types and non-key nodes
|
|
1213
1235
|
// only toplevel nodes (elements) can be key
|
|
1214
1236
|
forceToNotNull = !!(forceToNotNull && isKey && node.elements && !node.type);
|
|
1215
1237
|
|
|
1216
|
-
typeClone = cloneStructTypeForProxy(
|
|
1217
|
-
if(typeClone) {
|
|
1238
|
+
typeClone = cloneStructTypeForProxy(`${typeSchemaName}.${typeId}`);
|
|
1239
|
+
if (typeClone) {
|
|
1218
1240
|
// Recurse into elements of 'type' (if any)
|
|
1219
|
-
|
|
1241
|
+
if (typeClone.elements) {
|
|
1242
|
+
Object.entries(typeClone.elements).forEach(([ elemName, elem ]) => {
|
|
1220
1243
|
// if this is a foreign key element, we must check whether or not the association
|
|
1221
1244
|
// has been exposed as proxy. If it has not been exposed, no further structured
|
|
1222
1245
|
// types must be exposed as 'Proxy_' types.
|
|
1223
1246
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
(elem['@odata.foreignKey4'] && !typeClone.elements[elem['@odata.foreignKey4']].$exposed))
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1247
|
+
// TODO: expose types of assoc.keys and don't rely on exposed foreign keys
|
|
1248
|
+
if (!elem['@odata.foreignKey4'] ||
|
|
1249
|
+
(elem['@odata.foreignKey4'] && !typeClone.elements[elem['@odata.foreignKey4']].$exposed)) {
|
|
1250
|
+
exposeStructTypeForProxyOf(elem, `${typeId}_${elemName}`,
|
|
1251
|
+
typeSchemaName, isKey, forceToNotNull);
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
if (!typeDef.$proxyTypes)
|
|
1231
1256
|
typeDef.$proxyTypes = Object.create(null);
|
|
1232
1257
|
typeDef.$proxyTypes[globalSchemaPrefix] = typeClone;
|
|
1233
1258
|
}
|
|
@@ -1236,7 +1261,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1236
1261
|
// FUTURE: expose scalar type definition as well
|
|
1237
1262
|
}
|
|
1238
1263
|
}
|
|
1239
|
-
if(typeClone) {
|
|
1264
|
+
if (typeClone) {
|
|
1240
1265
|
// register the type clone at the proxy
|
|
1241
1266
|
// Reminder: Each proxy receives a full set of type clones, even if the types are shared
|
|
1242
1267
|
// (no scattered type clone caching). registerProxy() checks if a clone needs to be added to
|
|
@@ -1253,12 +1278,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1253
1278
|
// default, without we must do it to get the primary key tuple calculation correct.
|
|
1254
1279
|
// Remember: node.type is the service local type name (not prepended by the service name),
|
|
1255
1280
|
// so it can't be resolved in definitions later on
|
|
1256
|
-
if(typeClone.elements)
|
|
1281
|
+
if (typeClone.elements)
|
|
1257
1282
|
node.elements = typeClone.elements;
|
|
1258
1283
|
}
|
|
1259
1284
|
}
|
|
1260
1285
|
|
|
1261
|
-
function cloneStructTypeForProxy(
|
|
1286
|
+
function cloneStructTypeForProxy( name ) {
|
|
1262
1287
|
// Create type with empty elements
|
|
1263
1288
|
const type = {
|
|
1264
1289
|
kind: 'type',
|
|
@@ -1267,56 +1292,60 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1267
1292
|
};
|
|
1268
1293
|
setProp(type, '$mySchemaName', typeSchemaName);
|
|
1269
1294
|
setProp(type, '$exposedBy', 'proxyExposure');
|
|
1270
|
-
if(typeDef['@open'] !== undefined)
|
|
1295
|
+
if (typeDef['@open'] !== undefined)
|
|
1271
1296
|
type['@open'] = typeDef['@open'];
|
|
1272
1297
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1298
|
+
if (typeDef.elements) {
|
|
1299
|
+
Object.entries(typeDef.elements).forEach( ([ elemName, elem ]) => {
|
|
1300
|
+
if (!elem.target) {
|
|
1301
|
+
type.elements[elemName] = Object.create(null);
|
|
1302
|
+
Object.keys(elem).forEach((prop) => {
|
|
1303
|
+
type.elements[elemName][prop] = elem[prop];
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
else if (elem.keys && !elem.on) {
|
|
1279
1307
|
// a primary key can never be an unmanaged association
|
|
1280
|
-
|
|
1281
|
-
}
|
|
1282
|
-
if(forceToNotNull) {
|
|
1283
|
-
const newElt = type.elements[elemName];
|
|
1284
|
-
if(newElt.target) {
|
|
1285
|
-
if (newElt.cardinality === undefined)
|
|
1286
|
-
newElt.cardinality = {};
|
|
1287
|
-
newElt.cardinality.min = 1;
|
|
1308
|
+
type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
|
|
1288
1309
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1310
|
+
if (forceToNotNull) {
|
|
1311
|
+
const newElt = type.elements[elemName];
|
|
1312
|
+
if (newElt.target) {
|
|
1313
|
+
if (newElt.cardinality === undefined)
|
|
1314
|
+
newElt.cardinality = {};
|
|
1315
|
+
newElt.cardinality.min = 1;
|
|
1316
|
+
}
|
|
1317
|
+
// if odata-spec-violation-key-null is checking on min>1, this can be an else
|
|
1318
|
+
newElt.notNull = true;
|
|
1319
|
+
}
|
|
1320
|
+
setProp(type.elements[elemName], 'name', elem.name);
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1294
1323
|
return type;
|
|
1295
1324
|
}
|
|
1296
1325
|
}
|
|
1297
1326
|
|
|
1298
1327
|
// Convert a managed association into a structured type and
|
|
1299
1328
|
// eliminate nested foreign key associations
|
|
1300
|
-
function convertManagedAssocIntoStruct(e) {
|
|
1301
|
-
|
|
1329
|
+
function convertManagedAssocIntoStruct( e ) {
|
|
1330
|
+
const newElt = cloneCsnNonDict(e, options);
|
|
1302
1331
|
newElt.elements = Object.create(null);
|
|
1303
|
-
|
|
1332
|
+
// remove all unwanted garbage
|
|
1304
1333
|
delete newElt.keys;
|
|
1305
1334
|
delete newElt.target;
|
|
1306
1335
|
delete newElt.type;
|
|
1307
1336
|
// if this association has no keys or if it is a redirected parameterized entity,
|
|
1308
1337
|
// use the primary keys of the target
|
|
1309
|
-
|
|
1310
|
-
Object.keys(e._target.$keys).map(k => {
|
|
1311
|
-
keys.forEach(k => {
|
|
1338
|
+
const keys = (!e._target.$isParamEntity && e.keys) ||
|
|
1339
|
+
Object.keys(e._target.$keys).map(k => ({ ref: [ k ] }));
|
|
1340
|
+
keys.forEach((k) => {
|
|
1312
1341
|
let art = e._target || csnUtils.getCsnDef(e.target);
|
|
1313
|
-
for(
|
|
1342
|
+
for (const ps of k.ref)
|
|
1314
1343
|
art = art.elements[ps];
|
|
1315
|
-
|
|
1344
|
+
|
|
1316
1345
|
// art is in the target side, clone it and remove key property
|
|
1317
|
-
|
|
1346
|
+
const cloneArt = cloneCsnNonDict(art, options);
|
|
1318
1347
|
setProp(cloneArt, 'name', art.name);
|
|
1319
|
-
if(e.key)
|
|
1348
|
+
if (e.key)
|
|
1320
1349
|
cloneArt.notNull = true;
|
|
1321
1350
|
delete cloneArt.key;
|
|
1322
1351
|
newElt.elements[art.name] = cloneArt;
|
|
@@ -1330,28 +1359,27 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1330
1359
|
// the proxy that is just being created targets back into
|
|
1331
1360
|
// its own serice
|
|
1332
1361
|
// 2) or if no proxy for this source schema has been created yet
|
|
1333
|
-
function createProxyOrSchemaRefForManagedAssoc(e) {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
let newElt = cloneCsnNonDict(e, options);
|
|
1362
|
+
function createProxyOrSchemaRefForManagedAssoc( e ) {
|
|
1363
|
+
let newProxy = e._target;
|
|
1364
|
+
const newElt = cloneCsnNonDict(e, options);
|
|
1337
1365
|
|
|
1338
|
-
if(isProxyRequired(e)) {
|
|
1339
|
-
|
|
1340
|
-
if(!
|
|
1366
|
+
if (isProxyRequired(e)) {
|
|
1367
|
+
newProxy = getProxyForTargetOf(e);
|
|
1368
|
+
if (!newProxy) {
|
|
1341
1369
|
// option odataXServiceRefs has precedence over odataProxies
|
|
1342
|
-
if(e._target.$mySchemaName && options.odataXServiceRefs) {
|
|
1343
|
-
|
|
1370
|
+
if (e._target.$mySchemaName && options.odataXServiceRefs) {
|
|
1371
|
+
newProxy = createSchemaRefFor(e._target.$mySchemaName);
|
|
1344
1372
|
}
|
|
1345
|
-
else if(options.odataProxies) {
|
|
1346
|
-
|
|
1347
|
-
if(!e._target.$isParamEntity)
|
|
1348
|
-
populateProxyElements(e,
|
|
1373
|
+
else if (options.odataProxies) {
|
|
1374
|
+
newProxy = createProxyFor(e, e._target.$mySchemaName);
|
|
1375
|
+
if (!e._target.$isParamEntity)
|
|
1376
|
+
populateProxyElements(e, newProxy, getForeignKeyDefinitions(e));
|
|
1349
1377
|
}
|
|
1350
|
-
|
|
1378
|
+
newProxy = registerProxy(newProxy, e);
|
|
1351
1379
|
}
|
|
1352
1380
|
}
|
|
1353
|
-
if(
|
|
1354
|
-
|
|
1381
|
+
if (!newProxy) {
|
|
1382
|
+
newProxy = e._target;
|
|
1355
1383
|
// no proxy: no navigation
|
|
1356
1384
|
edmUtils.assignAnnotation(newElt, '@odata.navigable', false);
|
|
1357
1385
|
}
|
|
@@ -1367,9 +1395,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1367
1395
|
finalizeConstraintsOnAssoc(e);
|
|
1368
1396
|
setProp(newElt, '_constraints', e._constraints);
|
|
1369
1397
|
setProp(newElt, '_selfReferences', []);
|
|
1370
|
-
if(
|
|
1371
|
-
newElt.target =
|
|
1372
|
-
setProp(newElt, '_target',
|
|
1398
|
+
if (newProxy.kind === 'entity') {
|
|
1399
|
+
newElt.target = newProxy.name;
|
|
1400
|
+
setProp(newElt, '_target', newProxy);
|
|
1373
1401
|
}
|
|
1374
1402
|
return newElt;
|
|
1375
1403
|
}
|
|
@@ -1395,34 +1423,34 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1395
1423
|
the same service name 'S', which implies that they are always exposed
|
|
1396
1424
|
in the same Edm => no proxy required.
|
|
1397
1425
|
*/
|
|
1398
|
-
function isProxyRequired(element) {
|
|
1426
|
+
function isProxyRequired( element ) {
|
|
1399
1427
|
const targetSchemaName = element._target.$mySchemaName;
|
|
1400
1428
|
// longest match for service name
|
|
1401
|
-
return (!element._target.$proxy && globalSchemaPrefix !== targetSchemaName)
|
|
1402
|
-
((targetSchemaName &&
|
|
1403
|
-
globalSchemaPrefix === whatsMyServiceRootName(targetSchemaName))
|
|
1429
|
+
return (!element._target.$proxy && globalSchemaPrefix !== targetSchemaName)
|
|
1430
|
+
? (!((targetSchemaName &&
|
|
1431
|
+
globalSchemaPrefix === whatsMyServiceRootName(targetSchemaName)))) : false;
|
|
1404
1432
|
}
|
|
1405
1433
|
|
|
1406
1434
|
// read a proxy from the elements target
|
|
1407
|
-
function getProxyForTargetOf(element) {
|
|
1435
|
+
function getProxyForTargetOf( element ) {
|
|
1408
1436
|
return element._target.$cachedProxy && element._target.$cachedProxy[globalSchemaPrefix];
|
|
1409
1437
|
}
|
|
1410
1438
|
|
|
1411
1439
|
// register the proxy at the elements target
|
|
1412
|
-
function registerProxy(proxy, element) {
|
|
1413
|
-
if(proxy === undefined)
|
|
1440
|
+
function registerProxy( proxy, element ) {
|
|
1441
|
+
if (proxy === undefined)
|
|
1414
1442
|
return undefined;
|
|
1415
1443
|
|
|
1416
1444
|
setProp(proxy, '$globalSchemaPrefix', globalSchemaPrefix);
|
|
1417
1445
|
setProp(proxy, '$origin', element);
|
|
1418
1446
|
|
|
1419
|
-
const fqProxyName = proxy.$globalSchemaPrefix
|
|
1447
|
+
const fqProxyName = `${proxy.$globalSchemaPrefix}.${proxy.name}`;
|
|
1420
1448
|
|
|
1421
|
-
if(!element._target.$cachedProxy)
|
|
1449
|
+
if (!element._target.$cachedProxy)
|
|
1422
1450
|
edmUtils.assignProp(element._target, '$cachedProxy', Object.create(null));
|
|
1423
|
-
if(getProxyForTargetOf(element)) {
|
|
1424
|
-
info(null, ['definitions', struct.name, 'elements', element.name],
|
|
1425
|
-
|
|
1451
|
+
if (getProxyForTargetOf(element)) {
|
|
1452
|
+
info(null, [ 'definitions', struct.name, 'elements', element.name ],
|
|
1453
|
+
{ name: fqProxyName }, 'Proxy EDM entity type $(NAME) has already been registered');
|
|
1426
1454
|
}
|
|
1427
1455
|
else {
|
|
1428
1456
|
determineEntitySet(proxy);
|
|
@@ -1434,12 +1462,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1434
1462
|
}
|
|
1435
1463
|
|
|
1436
1464
|
function mergeProxiesIntoModel() {
|
|
1437
|
-
proxyCache.forEach(proxy => {
|
|
1465
|
+
proxyCache.forEach((proxy) => {
|
|
1466
|
+
const fqProxyName = `${proxy.$globalSchemaPrefix}.${proxy.name}`;
|
|
1467
|
+
const fqSchemaName = `${proxy.$globalSchemaPrefix}.${proxy.$mySchemaName}`;
|
|
1438
1468
|
|
|
1439
|
-
|
|
1440
|
-
const fqSchemaName = proxy.$globalSchemaPrefix + '.' + proxy.$mySchemaName;
|
|
1441
|
-
|
|
1442
|
-
if(proxy.kind === 'entity') {
|
|
1469
|
+
if (proxy.kind === 'entity') {
|
|
1443
1470
|
finalizeProxyContainments(proxy);
|
|
1444
1471
|
// collect all schemas even for newly exposed types
|
|
1445
1472
|
// (that may reside in another subcontext schema), but only once
|
|
@@ -1449,73 +1476,71 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1449
1476
|
// followed by all namespaces that are potentially exposed by the exposed types
|
|
1450
1477
|
// don't forget to prepend the global namespace prefix
|
|
1451
1478
|
// schemas are ordered in csn2edm.js for each service
|
|
1452
|
-
Object.keys(proxy.$exposedTypes).forEach(t =>
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
if(!schemas[schemaName]) {
|
|
1479
|
+
Object.keys(proxy.$exposedTypes).forEach(t => schemaSet.add(`${proxy.$globalSchemaPrefix}.${edmUtils.getSchemaPrefix(t)}`));
|
|
1480
|
+
schemaSet.forEach((schemaName) => {
|
|
1481
|
+
if (!schemas[schemaName]) {
|
|
1456
1482
|
schemas[schemaName] = { kind: 'schema', name: schemaName };
|
|
1457
1483
|
schemaNames.push(schemaName);
|
|
1458
1484
|
}
|
|
1459
1485
|
});
|
|
1460
1486
|
const alreadyRegistered = csn.definitions[fqProxyName];
|
|
1461
|
-
if(!alreadyRegistered) {
|
|
1487
|
+
if (!alreadyRegistered) {
|
|
1462
1488
|
csn.definitions[fqProxyName] = proxy;
|
|
1463
1489
|
reqDefs.definitions[fqProxyName] = proxy;
|
|
1464
|
-
setProp(proxy, '$path', ['definitions', fqProxyName]);
|
|
1465
|
-
Object.entries(proxy.$exposedTypes).forEach(([tn, v]) => {
|
|
1466
|
-
const fqtn = proxy.$globalSchemaPrefix
|
|
1467
|
-
if(csn.definitions[fqtn] === undefined) {
|
|
1490
|
+
setProp(proxy, '$path', [ 'definitions', fqProxyName ]);
|
|
1491
|
+
Object.entries(proxy.$exposedTypes).forEach(([ tn, v ]) => {
|
|
1492
|
+
const fqtn = `${proxy.$globalSchemaPrefix}.${tn}`;
|
|
1493
|
+
if (csn.definitions[fqtn] === undefined) {
|
|
1468
1494
|
csn.definitions[fqtn] = v;
|
|
1469
1495
|
reqDefs.definitions[fqtn] = v;
|
|
1470
|
-
setProp(v, '$path', ['definitions', fqtn]);
|
|
1496
|
+
setProp(v, '$path', [ 'definitions', fqtn ]);
|
|
1471
1497
|
}
|
|
1472
1498
|
});
|
|
1473
1499
|
|
|
1474
1500
|
// default location is not always correct in case proxy has been created by a nested assoc
|
|
1475
1501
|
// as foreign key targeting another proxy association
|
|
1476
|
-
let loc = ['definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name];
|
|
1477
|
-
if(proxy.$origin._parent.$path)
|
|
1478
|
-
loc = [...proxy.$origin._parent.$path, 'elements', proxy.$origin.name]
|
|
1502
|
+
let loc = [ 'definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name ];
|
|
1503
|
+
if (proxy.$origin._parent.$path)
|
|
1504
|
+
loc = [ ...proxy.$origin._parent.$path, 'elements', proxy.$origin.name ];
|
|
1479
1505
|
info(null, loc,
|
|
1480
|
-
|
|
1506
|
+
{ name: proxy.name }, 'Created proxy EDM entity type $(NAME)');
|
|
1481
1507
|
}
|
|
1482
|
-
else if(alreadyRegistered && !alreadyRegistered.$proxy &&
|
|
1508
|
+
else if (alreadyRegistered && !alreadyRegistered.$proxy &&
|
|
1483
1509
|
alreadyRegistered.kind !== 'entity') {
|
|
1484
|
-
warning('odata-definition-exists', ['definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name],
|
|
1485
|
-
|
|
1510
|
+
warning('odata-definition-exists', [ 'definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name ],
|
|
1511
|
+
{ '#': 'proxy', name: fqProxyName, kind: alreadyRegistered.kind });
|
|
1486
1512
|
}
|
|
1487
1513
|
}
|
|
1488
|
-
else {
|
|
1514
|
+
else if (!schemas[fqSchemaName]) {
|
|
1489
1515
|
// it's a service reference, just add that reference proxy
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
{ name: proxy.name }, 'Created EDM namespace reference $(NAME)');
|
|
1495
|
-
}
|
|
1496
|
-
// don't error on duplicate schemas, if it's already present then all is good....
|
|
1516
|
+
schemas[fqSchemaName] = proxy;
|
|
1517
|
+
schemaNames.push(fqSchemaName);
|
|
1518
|
+
info(null, [ 'definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name ],
|
|
1519
|
+
{ name: proxy.name }, 'Created EDM namespace reference $(NAME)');
|
|
1497
1520
|
}
|
|
1521
|
+
// don't error on duplicate schemas, if it's already present then all is good....
|
|
1498
1522
|
});
|
|
1499
|
-
schemaNames.sort((a,b) => b.length-a.length);
|
|
1523
|
+
schemaNames.sort((a, b) => b.length - a.length);
|
|
1500
1524
|
|
|
1501
|
-
function finalizeProxyContainments(proxy) {
|
|
1525
|
+
function finalizeProxyContainments( proxy ) {
|
|
1502
1526
|
// initialise containments after all exposed types are collected
|
|
1503
1527
|
// AND remove unfulfillable NavRestrictions
|
|
1504
1528
|
initContainments(proxy);
|
|
1505
1529
|
const assocPaths = proxy.$containeeAssociations.map(entry => entry.path.join('.'));
|
|
1506
1530
|
const newNpr = [];
|
|
1507
1531
|
const npr = proxy[NavResAnno];
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
proxy[NavResAnno] = newNpr;
|
|
1515
|
-
}
|
|
1516
|
-
else {
|
|
1517
|
-
delete proxy[NavResAnno]
|
|
1532
|
+
if (npr) {
|
|
1533
|
+
npr.forEach((np) => {
|
|
1534
|
+
const npath = np.NavigationProperty && np.NavigationProperty['='];
|
|
1535
|
+
if (npath && assocPaths.includes(npath))
|
|
1536
|
+
newNpr.push(np);
|
|
1537
|
+
});
|
|
1518
1538
|
}
|
|
1539
|
+
if (newNpr.length)
|
|
1540
|
+
proxy[NavResAnno] = newNpr;
|
|
1541
|
+
|
|
1542
|
+
else
|
|
1543
|
+
delete proxy[NavResAnno];
|
|
1519
1544
|
}
|
|
1520
1545
|
}
|
|
1521
1546
|
|
|
@@ -1532,25 +1557,25 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1532
1557
|
and do not render associations (this will include the foreign keys of
|
|
1533
1558
|
the _isToContainer association).
|
|
1534
1559
|
*/
|
|
1535
|
-
function initEdmKeyRefPaths(def, defName) {
|
|
1536
|
-
if(def.$keys) {
|
|
1560
|
+
function initEdmKeyRefPaths( def, defName ) {
|
|
1561
|
+
if (def.$keys) {
|
|
1537
1562
|
setProp(def, '$edmKeyPaths', []);
|
|
1538
1563
|
// for all key elements that shouldn't be ignored produce the paths
|
|
1539
1564
|
edmUtils.foreach(def.$keys, k => !(k._isToContainer && k._selfReferences.length), (k, kn) => {
|
|
1540
|
-
if(isEdmPropertyRendered(k, options) &&
|
|
1565
|
+
if (isEdmPropertyRendered(k, options) &&
|
|
1541
1566
|
!(options.isV2() && k['@Core.MediaType'])) {
|
|
1542
|
-
if(options.isV4() && options.isStructFormat) {
|
|
1567
|
+
if (options.isV4() && options.isStructFormat) {
|
|
1543
1568
|
// This is structured OData ONLY
|
|
1544
1569
|
// if the foreign keys are explicitly requested, ignore associations and use the flat foreign keys instead
|
|
1545
|
-
if(!options.renderForeignKeys || (options.renderForeignKeys && !k.target))
|
|
1570
|
+
if (!options.renderForeignKeys || (options.renderForeignKeys && !k.target))
|
|
1546
1571
|
def.$edmKeyPaths.push(...produceKeyRefPaths(k, kn, [ 'definitions', defName, 'elements', kn ]));
|
|
1547
1572
|
}
|
|
1548
1573
|
// In v2/v4 flat, associations are never rendered
|
|
1549
|
-
else if(!k.target) {
|
|
1550
|
-
def.$edmKeyPaths.push([kn]);
|
|
1574
|
+
else if (!k.target) {
|
|
1575
|
+
def.$edmKeyPaths.push([ kn ]);
|
|
1551
1576
|
}
|
|
1552
1577
|
// check toplevel key for spec violations
|
|
1553
|
-
checkKeySpecViolations(k, ['definitions', defName, 'elements', k.name]);
|
|
1578
|
+
checkKeySpecViolations(k, [ 'definitions', defName, 'elements', k.name ]);
|
|
1554
1579
|
}
|
|
1555
1580
|
});
|
|
1556
1581
|
}
|
|
@@ -1565,11 +1590,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1565
1590
|
navprops to be key ref.
|
|
1566
1591
|
If element is of scalar type, return it as an array.
|
|
1567
1592
|
*/
|
|
1568
|
-
function produceKeyRefPaths(eltCsn, prefix, path) {
|
|
1593
|
+
function produceKeyRefPaths( eltCsn, prefix, path ) {
|
|
1569
1594
|
const keyPaths = [];
|
|
1570
1595
|
// we want to point to the element in the entity which is the first path step
|
|
1571
|
-
const location = def.$path.concat(['elements']).concat(prefix.split('/')[0]);
|
|
1572
|
-
if(!isEdmPropertyRendered(eltCsn, options)) {
|
|
1596
|
+
const location = def.$path.concat([ 'elements' ]).concat(prefix.split('/')[0]);
|
|
1597
|
+
if (!isEdmPropertyRendered(eltCsn, options)) {
|
|
1573
1598
|
// let annos = Object.keys(eltCsn).filter(a=>a[0]==='@').join(', ');
|
|
1574
1599
|
// warning(null, ['definitions', struct.name, 'elements', eltCsn.name ],
|
|
1575
1600
|
// `${struct.name}: OData V4 primary key path: "${prefix}" is unexposed by one of these annotations "${annos}"` );
|
|
@@ -1583,25 +1608,25 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1583
1608
|
elements = finalType?.elements || finalType?.items?.elements;
|
|
1584
1609
|
}
|
|
1585
1610
|
if (elements) {
|
|
1586
|
-
Object.entries(elements).forEach(([eltName, elt]) => {
|
|
1587
|
-
if(!elt.$visited) {
|
|
1611
|
+
Object.entries(elements).forEach(([ eltName, elt ]) => {
|
|
1612
|
+
if (!elt.$visited) {
|
|
1588
1613
|
setProp(elt, '$visited', true);
|
|
1589
1614
|
let newRefs = [];
|
|
1590
1615
|
// if the foreign keys are explicitly requested, ignore associations and use the flat foreign key instead
|
|
1591
1616
|
// ignore nested unmanaged associations
|
|
1592
|
-
if((!options.renderForeignKeys || (options.renderForeignKeys && !elt.target)) && !(elt.target && elt.on))
|
|
1617
|
+
if ((!options.renderForeignKeys || (options.renderForeignKeys && !elt.target)) && !(elt.target && elt.on))
|
|
1593
1618
|
newRefs = produceKeyRefPaths(elt, prefix + options.pathDelimiter + eltName, path);
|
|
1594
|
-
if(newRefs.length) {
|
|
1619
|
+
if (newRefs.length) {
|
|
1595
1620
|
keyPaths.push(...newRefs);
|
|
1596
|
-
|
|
1621
|
+
// check path step key for spec violations
|
|
1597
1622
|
const pathSegment = `${prefix}/${eltName}`;
|
|
1598
1623
|
checkKeySpecViolations(elt, location, pathSegment);
|
|
1599
1624
|
}
|
|
1600
1625
|
delete elt.$visited;
|
|
1601
|
-
}
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1602
1628
|
error('odata-key-recursive', path, { name: prefix });
|
|
1603
1629
|
}
|
|
1604
|
-
|
|
1605
1630
|
});
|
|
1606
1631
|
}
|
|
1607
1632
|
/* If element is a managed association (can't be anything else),
|
|
@@ -1609,38 +1634,37 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1609
1634
|
This also implies that the association itself is never added into the
|
|
1610
1635
|
list of primary key refs
|
|
1611
1636
|
*/
|
|
1612
|
-
else if(eltCsn.target && !eltCsn.on) {
|
|
1637
|
+
else if (eltCsn.target && !eltCsn.on) {
|
|
1613
1638
|
// if this association has no keys or if it is a redirected parameterized entity,
|
|
1614
1639
|
// use the primary keys of the target
|
|
1615
|
-
|
|
1616
|
-
Object.keys(eltCsn._target.$keys).map(k => {
|
|
1617
|
-
let pathSegment = prefix
|
|
1618
|
-
keys.forEach(k => {
|
|
1640
|
+
const keys = (!eltCsn._target.$isParamEntity && eltCsn.keys) ||
|
|
1641
|
+
Object.keys(eltCsn._target.$keys).map(k => ({ ref: [ k ] }));
|
|
1642
|
+
let pathSegment = prefix;
|
|
1643
|
+
keys.forEach((k) => {
|
|
1619
1644
|
let art = eltCsn._target || csnUtils.getCsnDef(eltCsn.target);
|
|
1620
|
-
for(
|
|
1645
|
+
for (const ps of k.ref) {
|
|
1621
1646
|
art = art.elements[ps];
|
|
1622
|
-
pathSegment +=
|
|
1647
|
+
pathSegment += `/${art.name}`;
|
|
1623
1648
|
checkKeySpecViolations(art, location, pathSegment);
|
|
1624
|
-
if(art.type && !isBuiltinType(art.type))
|
|
1649
|
+
if (art.type && !isBuiltinType(art.type))
|
|
1625
1650
|
art = art._type || csnUtils.getCsnDef(art.type);
|
|
1626
|
-
}
|
|
1627
1651
|
}
|
|
1628
1652
|
keyPaths.push(...produceKeyRefPaths(art, prefix + options.pathDelimiter + k.ref.join(options.pathDelimiter), path));
|
|
1629
1653
|
});
|
|
1630
1654
|
}
|
|
1631
1655
|
else {
|
|
1632
|
-
keyPaths.push([prefix]);
|
|
1656
|
+
keyPaths.push([ prefix ]);
|
|
1633
1657
|
}
|
|
1634
1658
|
return keyPaths;
|
|
1635
1659
|
}
|
|
1636
1660
|
|
|
1637
|
-
function checkKeySpecViolations(elt, location, pathSegment) {
|
|
1661
|
+
function checkKeySpecViolations( elt, location, pathSegment ) {
|
|
1638
1662
|
// Nullability
|
|
1639
1663
|
const eltDef = elt.items || elt;
|
|
1640
|
-
if((!elt.key && (eltDef.notNull === undefined || eltDef.notNull === false)) ||
|
|
1664
|
+
if ((!elt.key && (eltDef.notNull === undefined || eltDef.notNull === false)) ||
|
|
1641
1665
|
elt.key && (eltDef.notNull !== undefined && eltDef.notNull === false)) {
|
|
1642
1666
|
message('odata-spec-violation-key-null', location,
|
|
1643
|
-
|
|
1667
|
+
{ name: pathSegment || elt.name, '#': !pathSegment ? 'std' : 'scalar' });
|
|
1644
1668
|
}
|
|
1645
1669
|
// many is either directly on elements or on the type
|
|
1646
1670
|
// due to added proxy types it might be that the type can't be found in definitions
|
|
@@ -1649,32 +1673,46 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1649
1673
|
!isBuiltinType(elt.type) &&
|
|
1650
1674
|
csn.definitions[elt.type] &&
|
|
1651
1675
|
csnUtils.getFinalTypeInfo(elt.type).items);
|
|
1652
|
-
if(type ||
|
|
1676
|
+
if (type ||
|
|
1653
1677
|
(options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
|
|
1654
1678
|
elt.cardinality?.max && elt.cardinality.max !== 1) {
|
|
1655
1679
|
// many primary key can be induced by a many parameter of a view
|
|
1656
1680
|
message('odata-spec-violation-key-array', location,
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1681
|
+
{
|
|
1682
|
+
name: pathSegment || elt.name,
|
|
1683
|
+
value: cardinality2str(elt),
|
|
1684
|
+
'#': elt.target ? 'assoc' : 'std',
|
|
1685
|
+
});
|
|
1662
1686
|
}
|
|
1663
1687
|
// type
|
|
1664
|
-
if(!elt.elements) {
|
|
1665
|
-
if(!type)
|
|
1688
|
+
if (!elt.elements) {
|
|
1689
|
+
if (!type)
|
|
1666
1690
|
type = isBuiltinType(elt.type) ? elt : csn.definitions[elt.type];
|
|
1667
1691
|
|
|
1668
1692
|
// check for legal scalar types, proxy exposed structured types are not resolvable in CSN
|
|
1669
1693
|
// V2 allows any Edm.PrimitiveType (even Double and Binary), V4 is more specific:
|
|
1670
|
-
if(options.isV4() && type && !type.target && isBuiltinType(type.type)) {
|
|
1694
|
+
if (options.isV4() && type && !type.target && isBuiltinType(type.type)) {
|
|
1671
1695
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1672
1696
|
const legalEdmTypes = {
|
|
1673
|
-
'Edm.Boolean':
|
|
1674
|
-
'Edm.
|
|
1675
|
-
|
|
1697
|
+
'Edm.Boolean': 1,
|
|
1698
|
+
'Edm.Byte': 1,
|
|
1699
|
+
'Edm.Date': 1,
|
|
1700
|
+
'Edm.DateTimeOffset': 1,
|
|
1701
|
+
'Edm.Decimal': 1,
|
|
1702
|
+
'Edm.Duration': 1,
|
|
1703
|
+
'Edm.Guid': 1,
|
|
1704
|
+
'Edm.Int16': 1,
|
|
1705
|
+
'Edm.Int32': 1,
|
|
1706
|
+
'Edm.Int64': 1,
|
|
1707
|
+
'Edm.SByte': 1,
|
|
1708
|
+
'Edm.String': 1,
|
|
1709
|
+
'Edm.TimeOfDay': 1,
|
|
1710
|
+
};
|
|
1711
|
+
if (!(edmType in legalEdmTypes)) {
|
|
1676
1712
|
warning('odata-spec-violation-key-type', location,
|
|
1677
|
-
|
|
1713
|
+
{
|
|
1714
|
+
name: pathSegment, type: type.type, id: edmType, '#': pathSegment ? 'std' : 'scalar',
|
|
1715
|
+
});
|
|
1678
1716
|
}
|
|
1679
1717
|
}
|
|
1680
1718
|
}
|
|
@@ -1708,26 +1746,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1708
1746
|
Path="items/subitems/subitems/up_" Target="Header/items/subitems"/>
|
|
1709
1747
|
Path="items/subitems/subitems/toG" Target="G"/>
|
|
1710
1748
|
*/
|
|
1711
|
-
function initEdmNavPropBindingTargets(def) {
|
|
1712
|
-
if(def.$hasEntitySet) {
|
|
1749
|
+
function initEdmNavPropBindingTargets( def ) {
|
|
1750
|
+
if (def.$hasEntitySet) {
|
|
1713
1751
|
forEachGeneric(def.items || def, 'elements', (element) => {
|
|
1714
|
-
produceTargetPath([edmUtils.getBaseName(def.name)], element, def);
|
|
1752
|
+
produceTargetPath([ edmUtils.getBaseName(def.name) ], element, def);
|
|
1715
1753
|
});
|
|
1716
1754
|
}
|
|
1717
1755
|
|
|
1718
|
-
function produceTargetPath(prefix, elt, curDef) {
|
|
1719
|
-
const newPrefix = [...prefix, elt.name];
|
|
1720
|
-
if(isEdmPropertyRendered(elt, options)) {
|
|
1756
|
+
function produceTargetPath( prefix, elt, curDef ) {
|
|
1757
|
+
const newPrefix = [ ...prefix, elt.name ];
|
|
1758
|
+
if (isEdmPropertyRendered(elt, options)) {
|
|
1721
1759
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1722
1760
|
// unroll derived type chains for assocs
|
|
1723
|
-
if(elt.target && !elt.$visited) {
|
|
1724
|
-
if(!elt._target.$edmTgtPaths)
|
|
1761
|
+
if (elt.target && !elt.$visited) {
|
|
1762
|
+
if (!elt._target.$edmTgtPaths)
|
|
1725
1763
|
setProp(elt._target, '$edmTgtPaths', []);
|
|
1726
1764
|
// drill into target only if
|
|
1727
1765
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1728
1766
|
// 2) current definition and target are the same (cycle)
|
|
1729
1767
|
// 3) it's no external reference
|
|
1730
|
-
if(!elt.$externalRef &&
|
|
1768
|
+
if (!elt.$externalRef &&
|
|
1731
1769
|
!elt._target.$hasEntitySet &&
|
|
1732
1770
|
!elt._isToContainer &&
|
|
1733
1771
|
curDef !== elt._target) {
|
|
@@ -1740,10 +1778,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1740
1778
|
}
|
|
1741
1779
|
else {
|
|
1742
1780
|
// try to find elements to drill down further
|
|
1743
|
-
while(elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1781
|
+
while (elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1744
1782
|
elt = csn.definitions[elt.type];
|
|
1745
|
-
|
|
1746
|
-
if(elt && elt.elements && !elt.$visited) {
|
|
1783
|
+
|
|
1784
|
+
if (elt && elt.elements && !elt.$visited) {
|
|
1747
1785
|
setProp(elt, '$visited', true);
|
|
1748
1786
|
Object.values(elt.elements).forEach(e => produceTargetPath(newPrefix, e, curDef));
|
|
1749
1787
|
delete elt.$visited;
|
|
@@ -1753,8 +1791,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1753
1791
|
}
|
|
1754
1792
|
}
|
|
1755
1793
|
|
|
1756
|
-
function initEdmNavPropBindingPaths(def) {
|
|
1757
|
-
if(options.isV4() &&def.$hasEntitySet) {
|
|
1794
|
+
function initEdmNavPropBindingPaths( def ) {
|
|
1795
|
+
if (options.isV4() && def.$hasEntitySet) {
|
|
1758
1796
|
let npbs = [];
|
|
1759
1797
|
forEachGeneric(def.items || def, 'elements', (element) => {
|
|
1760
1798
|
npbs = npbs.concat(produceNavigationPath(element, def));
|
|
@@ -1763,50 +1801,52 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1763
1801
|
}
|
|
1764
1802
|
|
|
1765
1803
|
// collect all paths originating from this element that end up in an entity set
|
|
1766
|
-
function produceNavigationPath(elt, curDef) {
|
|
1804
|
+
function produceNavigationPath( elt, curDef ) {
|
|
1767
1805
|
let npbs = [];
|
|
1768
1806
|
const prefix = elt.name;
|
|
1769
|
-
if(isEdmPropertyRendered(elt, options)) {
|
|
1807
|
+
if (isEdmPropertyRendered(elt, options)) {
|
|
1770
1808
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1771
1809
|
// unroll derived type chains for assocs
|
|
1772
|
-
if(elt.target && !elt.$visited) {
|
|
1810
|
+
if (elt.target && !elt.$visited) {
|
|
1773
1811
|
// drill into target only if
|
|
1774
1812
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1775
1813
|
// 2) current definition and target are the same (cycle)
|
|
1776
1814
|
// 3) it's no external reference
|
|
1777
|
-
if(!elt.$externalRef &&
|
|
1815
|
+
if (!elt.$externalRef &&
|
|
1778
1816
|
!elt._target.$hasEntitySet &&
|
|
1779
1817
|
!elt._isToContainer &&
|
|
1780
1818
|
curDef !== elt._target) {
|
|
1781
1819
|
// follow elements in the target but avoid cycles
|
|
1782
1820
|
setProp(elt, '$visited', true);
|
|
1783
|
-
Object.values(elt._target.elements).forEach(e =>
|
|
1821
|
+
Object.values(elt._target.elements).forEach((e) => {
|
|
1822
|
+
npbs = npbs.concat(produceNavigationPath(e, elt._target));
|
|
1823
|
+
});
|
|
1784
1824
|
delete elt.$visited;
|
|
1785
1825
|
}
|
|
1786
|
-
else if(!(options.odataContainment && options.isV4() && elt['@odata.contained'])) {
|
|
1826
|
+
else if (!(options.odataContainment && options.isV4() && elt['@odata.contained'])) {
|
|
1787
1827
|
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
1788
1828
|
// last assoc step must not be to-n and target a singleton
|
|
1789
|
-
let
|
|
1829
|
+
let path;
|
|
1790
1830
|
if (!elt.$externalRef &&
|
|
1791
1831
|
!(edmUtils.isToMany(elt) &&
|
|
1792
1832
|
edmUtils.isSingleton(elt._target) &&
|
|
1793
1833
|
options.isV4())) {
|
|
1794
|
-
if(elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1795
|
-
|
|
1834
|
+
if (elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1835
|
+
path = elt._target.$edmTgtPaths.find(p => p[0] === edmUtils.getBaseName(def.name)) || elt._target.$edmTgtPaths[0];
|
|
1796
1836
|
}
|
|
1797
|
-
else if(elt._target.$hasEntitySet) {
|
|
1837
|
+
else if (elt._target.$hasEntitySet) {
|
|
1798
1838
|
const baseName = edmUtils.getBaseName(elt._target.$entitySetName || elt._target.name);
|
|
1799
1839
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1800
1840
|
// if target is in another schema, target the full qualified entity set
|
|
1801
|
-
|
|
1802
|
-
[ baseName ] : [elt._target.$mySchemaName
|
|
1841
|
+
path = (elt._target.$mySchemaName === def.$mySchemaName)
|
|
1842
|
+
? [ baseName ] : [ `${elt._target.$mySchemaName}.EntityContainer`, baseName ];
|
|
1803
1843
|
}
|
|
1804
|
-
if(
|
|
1844
|
+
if (path) {
|
|
1805
1845
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1806
1846
|
// if target is in another schema, target the full qualified entity set
|
|
1807
1847
|
const npb = {
|
|
1808
1848
|
Path: elt.name,
|
|
1809
|
-
Target:
|
|
1849
|
+
Target: path.join('/'),
|
|
1810
1850
|
};
|
|
1811
1851
|
npbs.push( npb );
|
|
1812
1852
|
}
|
|
@@ -1817,61 +1857,65 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1817
1857
|
}
|
|
1818
1858
|
else {
|
|
1819
1859
|
// try to find elements to drill down further
|
|
1820
|
-
while(elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1860
|
+
while (elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1821
1861
|
elt = csn.definitions[elt.type];
|
|
1822
|
-
|
|
1823
|
-
if(elt && elt.elements && !elt.$visited) {
|
|
1862
|
+
|
|
1863
|
+
if (elt && elt.elements && !elt.$visited) {
|
|
1824
1864
|
setProp(elt, '$visited', true);
|
|
1825
|
-
Object.values(elt.elements).forEach(e =>
|
|
1865
|
+
Object.values(elt.elements).forEach((e) => {
|
|
1866
|
+
npbs = npbs.concat(produceNavigationPath(e, curDef));
|
|
1867
|
+
});
|
|
1826
1868
|
delete elt.$visited;
|
|
1827
1869
|
}
|
|
1828
1870
|
}
|
|
1829
1871
|
}
|
|
1830
|
-
npbs.forEach(p =>
|
|
1872
|
+
npbs.forEach((p) => {
|
|
1873
|
+
p.Path = `${prefix}/${p.Path}`;
|
|
1874
|
+
});
|
|
1831
1875
|
return npbs;
|
|
1832
1876
|
}
|
|
1833
1877
|
}
|
|
1834
1878
|
|
|
1835
|
-
function determineEntitySet(def) {
|
|
1879
|
+
function determineEntitySet( def ) {
|
|
1836
1880
|
// if this is an entity or a view, determine if an entity set is required or not
|
|
1837
1881
|
// 1) must not be a proxy and not a containee in V4
|
|
1838
1882
|
// No annos are rendered for non-existing EntitySet targets.
|
|
1839
|
-
if(def.$hasEntitySet === undefined) {
|
|
1883
|
+
if (def.$hasEntitySet === undefined) {
|
|
1840
1884
|
const hasEntitySet = def.kind === 'entity' && !(options.isV4() && edmUtils.isContainee(def)) && !def.$proxy;
|
|
1841
1885
|
setProp(def, '$hasEntitySet', hasEntitySet);
|
|
1842
1886
|
}
|
|
1843
1887
|
}
|
|
1844
1888
|
|
|
1845
|
-
function finalize(def, defName) {
|
|
1889
|
+
function finalize( def, defName ) {
|
|
1846
1890
|
// 1. let all doc props become @Core.Descriptions
|
|
1847
1891
|
// 2. mark a member that will become a collection
|
|
1848
1892
|
// 3. assign the edm primitive type to elements, to be used in the rendering later
|
|
1849
1893
|
// 4. assign @Validation.AllowedValues to enums
|
|
1850
|
-
const
|
|
1894
|
+
const defLocation = [ 'definitions', defName ];
|
|
1851
1895
|
edmUtils.assignAnnotation(def, '@Core.Description', def.doc);
|
|
1852
1896
|
markCollection(def);
|
|
1853
1897
|
mapCdsToEdmProp(def);
|
|
1854
|
-
annotateAllowedValues(def,
|
|
1855
|
-
if (def.returns)
|
|
1898
|
+
annotateAllowedValues(def, defLocation);
|
|
1899
|
+
if (def.returns) {
|
|
1856
1900
|
markCollection(def.returns);
|
|
1857
1901
|
mapCdsToEdmProp(def.returns);
|
|
1858
|
-
annotateAllowedValues(def.returns, [...
|
|
1902
|
+
annotateAllowedValues(def.returns, [ ...defLocation, 'returns' ]);
|
|
1859
1903
|
}
|
|
1860
|
-
forEachMemberRecursively(def, (member, _memberName, _prop,
|
|
1904
|
+
forEachMemberRecursively(def, (member, _memberName, _prop, location) => {
|
|
1861
1905
|
edmUtils.assignAnnotation(member, '@Core.Description', member.doc);
|
|
1862
1906
|
markCollection(member);
|
|
1863
1907
|
mapCdsToEdmProp(member);
|
|
1864
|
-
annotateAllowedValues(member,
|
|
1908
|
+
annotateAllowedValues(member, location);
|
|
1865
1909
|
ComputedDefaultValue(member);
|
|
1866
1910
|
if (member.returns) {
|
|
1867
1911
|
edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
|
|
1868
1912
|
markCollection(member.returns);
|
|
1869
1913
|
mapCdsToEdmProp(member.returns);
|
|
1870
|
-
annotateAllowedValues(member.returns, [...
|
|
1914
|
+
annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
|
|
1871
1915
|
}
|
|
1872
|
-
},
|
|
1916
|
+
}, defLocation);
|
|
1873
1917
|
// mark members that need to be rendered as collections
|
|
1874
|
-
function markCollection(obj) {
|
|
1918
|
+
function markCollection( obj ) {
|
|
1875
1919
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
1876
1920
|
if (items) {
|
|
1877
1921
|
edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : true);
|
|
@@ -1886,48 +1930,46 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1886
1930
|
- has no value but the base type is cds.String, use the
|
|
1887
1931
|
symbol as value
|
|
1888
1932
|
*/
|
|
1889
|
-
function annotateAllowedValues(node,
|
|
1933
|
+
function annotateAllowedValues( node, location ) {
|
|
1890
1934
|
let typeDef = node;
|
|
1891
|
-
if(!node.enum && node.type && !isBuiltinType(node.type))
|
|
1935
|
+
if (!node.enum && node.type && !isBuiltinType(node.type))
|
|
1892
1936
|
typeDef = csn.definitions[node.type];
|
|
1893
|
-
if(typeDef?.enum) {
|
|
1937
|
+
if (typeDef?.enum) {
|
|
1894
1938
|
const enumValue = [];
|
|
1895
|
-
for(const enumSymbol in typeDef.enum) {
|
|
1939
|
+
for (const enumSymbol in typeDef.enum) {
|
|
1896
1940
|
const result = { '@Core.SymbolicName': enumSymbol };
|
|
1897
1941
|
let enumSymbolDef = typeDef.enum[enumSymbol];
|
|
1898
|
-
while(enumSymbolDef && !enumSymbolDef.$visited && enumSymbolDef['#']) {
|
|
1942
|
+
while (enumSymbolDef && !enumSymbolDef.$visited && enumSymbolDef['#']) {
|
|
1899
1943
|
setProp(enumSymbolDef, '$visited', true);
|
|
1900
1944
|
enumSymbolDef = typeDef.enum[enumSymbolDef['#']];
|
|
1901
1945
|
}
|
|
1902
1946
|
// reset visited
|
|
1903
|
-
for(const es in typeDef.enum)
|
|
1947
|
+
for (const es in typeDef.enum)
|
|
1904
1948
|
delete typeDef.enum[es].$visited;
|
|
1905
1949
|
|
|
1906
|
-
if(enumSymbolDef) {
|
|
1907
|
-
if(enumSymbolDef.val !== undefined) {
|
|
1950
|
+
if (enumSymbolDef) {
|
|
1951
|
+
if (enumSymbolDef.val !== undefined) {
|
|
1908
1952
|
// 'null' value is represented spec conform as empty record in AllowedValues collection
|
|
1909
1953
|
result.Value = enumSymbolDef.val;
|
|
1910
1954
|
enumValue.push(result);
|
|
1911
1955
|
}
|
|
1912
|
-
else {
|
|
1913
|
-
if(typeDef.type === 'cds.String') {
|
|
1956
|
+
else if (typeDef.type === 'cds.String') {
|
|
1914
1957
|
// the symbol is used as value for type 'cds.String'
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
}
|
|
1958
|
+
result.Value = enumSymbol;
|
|
1959
|
+
enumValue.push(result);
|
|
1960
|
+
}
|
|
1961
|
+
else if (node.kind !== 'annotation') {
|
|
1962
|
+
// omit the entry and warn
|
|
1963
|
+
warning('odata-enum-missing-value', location,
|
|
1964
|
+
{ name: enumSymbol, anno: '@Validation.AllowedValues', type: typeDef.type },
|
|
1965
|
+
'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
|
|
1924
1966
|
}
|
|
1925
1967
|
}
|
|
1926
1968
|
else { // enumSymbolDef not found
|
|
1927
1969
|
// omit the entry and warn
|
|
1928
|
-
warning('odata-enum-missing-value',
|
|
1929
|
-
|
|
1930
|
-
|
|
1970
|
+
warning('odata-enum-missing-value', location,
|
|
1971
|
+
{ name: enumSymbol, anno: '@Validation.AllowedValues', type: typeDef.type },
|
|
1972
|
+
'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
|
|
1931
1973
|
}
|
|
1932
1974
|
|
|
1933
1975
|
// Can't rely that @description has already been renamed to @Core.Description
|
|
@@ -1937,11 +1979,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1937
1979
|
if (desc)
|
|
1938
1980
|
result['@Core.Description'] = desc;
|
|
1939
1981
|
}
|
|
1940
|
-
if(enumValue.length > 0)
|
|
1982
|
+
if (enumValue.length > 0)
|
|
1941
1983
|
edmUtils.assignAnnotation(node, '@Validation.AllowedValues', enumValue);
|
|
1942
1984
|
}
|
|
1943
1985
|
}
|
|
1944
|
-
|
|
1945
1986
|
}
|
|
1946
1987
|
|
|
1947
1988
|
// If containment in V4 is active, annotations that would be assigned to the containees
|
|
@@ -1949,48 +1990,47 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1949
1990
|
// the containment navigation property.
|
|
1950
1991
|
// Today only Capabilities.*Restrictions are known to be remapped as there exists a CDS
|
|
1951
1992
|
// short cut annotation @readonly that gets expanded and can be safely remapped.
|
|
1952
|
-
function pullupCapabilitiesAnnotations(rootContainer) {
|
|
1953
|
-
|
|
1954
|
-
if(!options.odataCapabilitiesPullup)
|
|
1993
|
+
function pullupCapabilitiesAnnotations( rootContainer ) {
|
|
1994
|
+
if (!options.odataCapabilitiesPullup)
|
|
1955
1995
|
return;
|
|
1956
1996
|
// @Capabilities is applicable to EntitySet/Collection only
|
|
1957
|
-
if(!rootContainer.$hasEntitySet)
|
|
1997
|
+
if (!rootContainer.$hasEntitySet)
|
|
1958
1998
|
return;
|
|
1959
1999
|
|
|
1960
|
-
const isRecursiveContainment
|
|
1961
|
-
!!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
2000
|
+
const isRecursiveContainment
|
|
2001
|
+
= !!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
1962
2002
|
rootContainer.$containerNames.length === 1 &&
|
|
1963
2003
|
rootContainer.$containeeAssociations.some(entry => rootContainer.$containerNames.includes(entry.assoc.target)));
|
|
1964
2004
|
|
|
1965
2005
|
// Root nodes are not contained
|
|
1966
|
-
const isRootNode
|
|
1967
|
-
!!(!rootContainer.$containerNames ||
|
|
2006
|
+
const isRootNode
|
|
2007
|
+
= !!(!rootContainer.$containerNames ||
|
|
1968
2008
|
rootContainer.$containerNames && rootContainer.$containerNames.length === 0);
|
|
1969
2009
|
|
|
1970
|
-
if(!isRecursiveContainment && !isRootNode)
|
|
2010
|
+
if (!isRecursiveContainment && !isRootNode)
|
|
1971
2011
|
return;
|
|
1972
2012
|
|
|
1973
2013
|
const rootRestrictions = [];
|
|
1974
2014
|
addContainmentAnnotationsRecursively([], rootContainer);
|
|
1975
|
-
if(rootRestrictions.length)
|
|
2015
|
+
if (rootRestrictions.length)
|
|
1976
2016
|
rootContainer[NavResAnno] = rootRestrictions;
|
|
1977
2017
|
|
|
1978
|
-
function addContainmentAnnotationsRecursively(prefix, container) {
|
|
1979
|
-
if(container.$containeeAssociations) {
|
|
2018
|
+
function addContainmentAnnotationsRecursively( prefix, container ) {
|
|
2019
|
+
if (container.$containeeAssociations) {
|
|
1980
2020
|
// copy or create container restrictions, don't modify original
|
|
1981
|
-
const localRestrictions = container[NavResAnno]
|
|
1982
|
-
cloneAnnotationValue(container[NavResAnno]) : []
|
|
2021
|
+
const localRestrictions = container[NavResAnno]
|
|
2022
|
+
? cloneAnnotationValue(container[NavResAnno]) : [];
|
|
1983
2023
|
|
|
1984
2024
|
// prefix the existing navigation property restrictions on the container
|
|
1985
|
-
if(prefix.length) {
|
|
1986
|
-
localRestrictions.forEach(npe => {
|
|
1987
|
-
if(npe.NavigationProperty &&
|
|
2025
|
+
if (prefix.length) {
|
|
2026
|
+
localRestrictions.forEach((npe) => {
|
|
2027
|
+
if (npe.NavigationProperty &&
|
|
1988
2028
|
npe.NavigationProperty['='] &&
|
|
1989
2029
|
typeof npe.NavigationProperty['='] === 'string') {
|
|
1990
|
-
applyTransformations({ definitions: { npe }}, {
|
|
2030
|
+
applyTransformations({ definitions: { npe } }, {
|
|
1991
2031
|
'=': (parent, prop, value) => {
|
|
1992
2032
|
parent[prop] = prefix.concat(value).join('.');
|
|
1993
|
-
}
|
|
2033
|
+
},
|
|
1994
2034
|
});
|
|
1995
2035
|
}
|
|
1996
2036
|
});
|
|
@@ -1998,34 +2038,32 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1998
2038
|
|
|
1999
2039
|
setProp(container, '$visited', true);
|
|
2000
2040
|
// collect capabilities from containees
|
|
2001
|
-
container.$containeeAssociations.forEach(entry => {
|
|
2041
|
+
container.$containeeAssociations.forEach((entry) => {
|
|
2002
2042
|
const { assoc, path } = entry;
|
|
2003
2043
|
const containee = assoc._target;
|
|
2004
2044
|
|
|
2005
|
-
if(edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
2045
|
+
if (edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
2006
2046
|
const localAssocPath = path.join('.');
|
|
2007
|
-
let navPropEntry= localRestrictions.find(p =>
|
|
2008
|
-
p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
|
|
2047
|
+
let navPropEntry = localRestrictions.find(p => p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
|
|
2009
2048
|
const hasEntry = !!navPropEntry;
|
|
2010
2049
|
|
|
2011
|
-
if(!hasEntry)
|
|
2012
|
-
navPropEntry =
|
|
2013
|
-
|
|
2050
|
+
if (!hasEntry)
|
|
2051
|
+
navPropEntry = { NavigationProperty: { '=': prefix.concat(localAssocPath).join('.') } };
|
|
2052
|
+
|
|
2014
2053
|
|
|
2015
2054
|
const props = Object.entries(containee);
|
|
2016
2055
|
let newEntry = false;
|
|
2017
|
-
capabilities.forEach(c => {
|
|
2018
|
-
if(edmUtils.mergeIntoNavPropEntry(c, navPropEntry, prefix.concat(path), props))
|
|
2056
|
+
capabilities.forEach((c) => {
|
|
2057
|
+
if (edmUtils.mergeIntoNavPropEntry(c, navPropEntry, prefix.concat(path), props))
|
|
2019
2058
|
newEntry = true;
|
|
2020
2059
|
});
|
|
2021
2060
|
|
|
2022
|
-
if(newEntry && !hasEntry)
|
|
2061
|
+
if (newEntry && !hasEntry)
|
|
2023
2062
|
localRestrictions.push(navPropEntry);
|
|
2024
|
-
}
|
|
2025
2063
|
|
|
2026
|
-
|
|
2064
|
+
|
|
2065
|
+
if (!containee.$visited)
|
|
2027
2066
|
addContainmentAnnotationsRecursively(prefix.concat(path), containee);
|
|
2028
|
-
}
|
|
2029
2067
|
}
|
|
2030
2068
|
});
|
|
2031
2069
|
|
|
@@ -2046,95 +2084,97 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
2046
2084
|
parameter, a warning is raised, Core.OptionalParameter requires that all optional
|
|
2047
2085
|
parameters appear rightmost.
|
|
2048
2086
|
*/
|
|
2049
|
-
function annotateOptionalActFuncParams(def, defName) {
|
|
2050
|
-
if(!isBetaEnabled(options, 'optionalActionFunctionParameters'))
|
|
2087
|
+
function annotateOptionalActFuncParams( def, defName ) {
|
|
2088
|
+
if (!isBetaEnabled(options, 'optionalActionFunctionParameters'))
|
|
2051
2089
|
return;
|
|
2052
2090
|
// return if there is nothing to do
|
|
2053
|
-
const loc = [ 'definitions', defName ]
|
|
2054
|
-
if(def.kind === 'function' || def.kind === 'action')
|
|
2091
|
+
const loc = [ 'definitions', defName ];
|
|
2092
|
+
if (def.kind === 'function' || def.kind === 'action')
|
|
2055
2093
|
iterateParams(def, loc.concat('params'));
|
|
2056
|
-
if(def.actions) {
|
|
2057
|
-
for(const an in def.actions) {
|
|
2094
|
+
if (def.actions) {
|
|
2095
|
+
for (const an in def.actions) {
|
|
2058
2096
|
const a = def.actions[an];
|
|
2059
|
-
iterateParams(a, loc.concat(['actions', an, 'params' ]));
|
|
2097
|
+
iterateParams(a, loc.concat([ 'actions', an, 'params' ]));
|
|
2060
2098
|
}
|
|
2061
2099
|
}
|
|
2062
2100
|
|
|
2063
|
-
function iterateParams(
|
|
2101
|
+
function iterateParams( action, location ) {
|
|
2064
2102
|
let optPns = [];
|
|
2065
|
-
|
|
2103
|
+
if (action.params) {
|
|
2104
|
+
Object.values(action.params).forEach((p) => {
|
|
2066
2105
|
// user assigned annotation, don't touch it
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2106
|
+
if (Object.keys(p).some(a => a.startsWith('@Core.OptionalParameter') && p[a] !== null)) {
|
|
2107
|
+
optPns.push(p);
|
|
2108
|
+
}
|
|
2109
|
+
// default value automatically makes param optional
|
|
2110
|
+
else if (p.default) {
|
|
2111
|
+
if (p.default.val)
|
|
2112
|
+
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.DefaultValue', p.default.val);
|
|
2113
|
+
else
|
|
2114
|
+
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2115
|
+
optPns.push(p);
|
|
2116
|
+
}
|
|
2117
|
+
// if no default is available, nullable makes param optional
|
|
2118
|
+
else if (!p.notNull) {
|
|
2075
2119
|
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
else if(!p.notNull) {
|
|
2080
|
-
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2081
|
-
optPns.push(p);
|
|
2082
|
-
|
|
2083
|
-
}
|
|
2084
|
-
else {
|
|
2120
|
+
optPns.push(p);
|
|
2121
|
+
}
|
|
2122
|
+
else {
|
|
2085
2123
|
// this is a mandatory parameter, warn about all previously collected optional parameters
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2124
|
+
optPns.forEach((op) => {
|
|
2125
|
+
const type = op.items?.type || op.type;
|
|
2126
|
+
if (type !== special$self)
|
|
2127
|
+
warning('odata-parameter-order', location.concat(op.name));
|
|
2128
|
+
});
|
|
2129
|
+
optPns = [];
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2094
2133
|
}
|
|
2095
2134
|
}
|
|
2096
2135
|
|
|
2097
|
-
|
|
2136
|
+
// ////////////////////////////////////////////////////////////////////
|
|
2098
2137
|
//
|
|
2099
2138
|
// Helper section starts here
|
|
2100
2139
|
//
|
|
2101
2140
|
|
|
2102
2141
|
|
|
2103
|
-
function mapCdsToEdmProp(obj) {
|
|
2142
|
+
function mapCdsToEdmProp( obj ) {
|
|
2104
2143
|
if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
|
|
2105
|
-
|
|
2144
|
+
const edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
2106
2145
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2146
|
+
}
|
|
2147
|
+
else if (obj._isCollection && (obj.items && isBuiltinType(csnUtils.getFinalTypeInfo(obj.items.type)?.type))) {
|
|
2148
|
+
const edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType'], obj.$path);
|
|
2109
2149
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2110
2150
|
}
|
|
2111
2151
|
// This is the special case when we have array of array, but will not be supported in the future
|
|
2112
2152
|
else if (obj._isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(csnUtils.getFinalTypeInfo(obj.items.items.type)?.type)) {
|
|
2113
|
-
|
|
2153
|
+
const edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
2114
2154
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2115
2155
|
}
|
|
2116
2156
|
}
|
|
2117
2157
|
|
|
2118
|
-
function ComputedDefaultValue(member) {
|
|
2158
|
+
function ComputedDefaultValue( member ) {
|
|
2119
2159
|
if (member.default && !csn['@Core.ComputedDefaultValue']) {
|
|
2120
2160
|
let def = member.default;
|
|
2121
2161
|
let noTailExpr = false;
|
|
2122
|
-
if(def.xpr) {
|
|
2162
|
+
if (def.xpr) {
|
|
2123
2163
|
let i = 0;
|
|
2124
2164
|
// consume all unary signs
|
|
2125
|
-
while(def.xpr[i] === '-' || def.xpr[i] === '+')
|
|
2165
|
+
while (def.xpr[i] === '-' || def.xpr[i] === '+')
|
|
2166
|
+
i++;
|
|
2126
2167
|
// noTailExpr is true if there is nothing behind the next token in the stream
|
|
2127
|
-
noTailExpr = i < def.xpr.length-1;
|
|
2168
|
+
noTailExpr = i < def.xpr.length - 1;
|
|
2128
2169
|
def = def.xpr[i];
|
|
2129
2170
|
}
|
|
2130
2171
|
// it is a computed value if it is not a simple value or an annotation
|
|
2131
|
-
if(!((def.val !== undefined && !noTailExpr) || def['#']))
|
|
2172
|
+
if (!((def.val !== undefined && !noTailExpr) || def['#']))
|
|
2132
2173
|
edmUtils.assignAnnotation(member, '@Core.ComputedDefaultValue', true);
|
|
2133
|
-
}
|
|
2134
2174
|
}
|
|
2135
2175
|
}
|
|
2136
2176
|
}
|
|
2137
2177
|
|
|
2138
2178
|
module.exports = {
|
|
2139
2179
|
initializeModel,
|
|
2140
|
-
}
|
|
2180
|
+
};
|