@sap/cds-compiler 4.0.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- 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 +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- 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
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -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
|
|
@@ -975,8 +996,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
975
996
|
edmUtils.foreach(struct.elements,
|
|
976
997
|
e =>
|
|
977
998
|
e['@odata.foreignKey4'] === element.name,
|
|
978
|
-
e => e
|
|
979
|
-
element
|
|
999
|
+
e => e.$ignore = true);
|
|
1000
|
+
element.$ignore = true;
|
|
980
1001
|
info(null, ['definitions', struct.name, 'elements', element.name]
|
|
981
1002
|
`${element.type.replace('cds.', '')} "${element.name}" excluded,
|
|
982
1003
|
target "${element._target.name}" is annotated '@cds.autoexpose: ${element._target['@cds.autoexpose']}'`
|
|
@@ -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,24 +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
|
|
1617
|
+
if ((!options.renderForeignKeys || (options.renderForeignKeys && !elt.target)) && !(elt.target && elt.on))
|
|
1592
1618
|
newRefs = produceKeyRefPaths(elt, prefix + options.pathDelimiter + eltName, path);
|
|
1593
|
-
if(newRefs.length) {
|
|
1619
|
+
if (newRefs.length) {
|
|
1594
1620
|
keyPaths.push(...newRefs);
|
|
1595
|
-
|
|
1621
|
+
// check path step key for spec violations
|
|
1596
1622
|
const pathSegment = `${prefix}/${eltName}`;
|
|
1597
1623
|
checkKeySpecViolations(elt, location, pathSegment);
|
|
1598
1624
|
}
|
|
1599
1625
|
delete elt.$visited;
|
|
1600
|
-
}
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1601
1628
|
error('odata-key-recursive', path, { name: prefix });
|
|
1602
1629
|
}
|
|
1603
|
-
|
|
1604
1630
|
});
|
|
1605
1631
|
}
|
|
1606
1632
|
/* If element is a managed association (can't be anything else),
|
|
@@ -1608,38 +1634,37 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1608
1634
|
This also implies that the association itself is never added into the
|
|
1609
1635
|
list of primary key refs
|
|
1610
1636
|
*/
|
|
1611
|
-
else if(eltCsn.target && !eltCsn.on) {
|
|
1637
|
+
else if (eltCsn.target && !eltCsn.on) {
|
|
1612
1638
|
// if this association has no keys or if it is a redirected parameterized entity,
|
|
1613
1639
|
// use the primary keys of the target
|
|
1614
|
-
|
|
1615
|
-
Object.keys(eltCsn._target.$keys).map(k => {
|
|
1616
|
-
let pathSegment = prefix
|
|
1617
|
-
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) => {
|
|
1618
1644
|
let art = eltCsn._target || csnUtils.getCsnDef(eltCsn.target);
|
|
1619
|
-
for(
|
|
1645
|
+
for (const ps of k.ref) {
|
|
1620
1646
|
art = art.elements[ps];
|
|
1621
|
-
pathSegment +=
|
|
1647
|
+
pathSegment += `/${art.name}`;
|
|
1622
1648
|
checkKeySpecViolations(art, location, pathSegment);
|
|
1623
|
-
if(art.type && !isBuiltinType(art.type))
|
|
1649
|
+
if (art.type && !isBuiltinType(art.type))
|
|
1624
1650
|
art = art._type || csnUtils.getCsnDef(art.type);
|
|
1625
|
-
}
|
|
1626
1651
|
}
|
|
1627
1652
|
keyPaths.push(...produceKeyRefPaths(art, prefix + options.pathDelimiter + k.ref.join(options.pathDelimiter), path));
|
|
1628
1653
|
});
|
|
1629
1654
|
}
|
|
1630
1655
|
else {
|
|
1631
|
-
keyPaths.push([prefix]);
|
|
1656
|
+
keyPaths.push([ prefix ]);
|
|
1632
1657
|
}
|
|
1633
1658
|
return keyPaths;
|
|
1634
1659
|
}
|
|
1635
1660
|
|
|
1636
|
-
function checkKeySpecViolations(elt, location, pathSegment) {
|
|
1661
|
+
function checkKeySpecViolations( elt, location, pathSegment ) {
|
|
1637
1662
|
// Nullability
|
|
1638
1663
|
const eltDef = elt.items || elt;
|
|
1639
|
-
if((!elt.key && (eltDef.notNull === undefined || eltDef.notNull === false)) ||
|
|
1664
|
+
if ((!elt.key && (eltDef.notNull === undefined || eltDef.notNull === false)) ||
|
|
1640
1665
|
elt.key && (eltDef.notNull !== undefined && eltDef.notNull === false)) {
|
|
1641
1666
|
message('odata-spec-violation-key-null', location,
|
|
1642
|
-
|
|
1667
|
+
{ name: pathSegment || elt.name, '#': !pathSegment ? 'std' : 'scalar' });
|
|
1643
1668
|
}
|
|
1644
1669
|
// many is either directly on elements or on the type
|
|
1645
1670
|
// due to added proxy types it might be that the type can't be found in definitions
|
|
@@ -1648,32 +1673,46 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1648
1673
|
!isBuiltinType(elt.type) &&
|
|
1649
1674
|
csn.definitions[elt.type] &&
|
|
1650
1675
|
csnUtils.getFinalTypeInfo(elt.type).items);
|
|
1651
|
-
if(type ||
|
|
1676
|
+
if (type ||
|
|
1652
1677
|
(options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
|
|
1653
1678
|
elt.cardinality?.max && elt.cardinality.max !== 1) {
|
|
1654
1679
|
// many primary key can be induced by a many parameter of a view
|
|
1655
1680
|
message('odata-spec-violation-key-array', location,
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1681
|
+
{
|
|
1682
|
+
name: pathSegment || elt.name,
|
|
1683
|
+
value: cardinality2str(elt),
|
|
1684
|
+
'#': elt.target ? 'assoc' : 'std',
|
|
1685
|
+
});
|
|
1661
1686
|
}
|
|
1662
1687
|
// type
|
|
1663
|
-
if(!elt.elements) {
|
|
1664
|
-
if(!type)
|
|
1688
|
+
if (!elt.elements) {
|
|
1689
|
+
if (!type)
|
|
1665
1690
|
type = isBuiltinType(elt.type) ? elt : csn.definitions[elt.type];
|
|
1666
1691
|
|
|
1667
1692
|
// check for legal scalar types, proxy exposed structured types are not resolvable in CSN
|
|
1668
1693
|
// V2 allows any Edm.PrimitiveType (even Double and Binary), V4 is more specific:
|
|
1669
|
-
if(options.isV4() && type && !type.target && isBuiltinType(type.type)) {
|
|
1694
|
+
if (options.isV4() && type && !type.target && isBuiltinType(type.type)) {
|
|
1670
1695
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1671
1696
|
const legalEdmTypes = {
|
|
1672
|
-
'Edm.Boolean':
|
|
1673
|
-
'Edm.
|
|
1674
|
-
|
|
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)) {
|
|
1675
1712
|
warning('odata-spec-violation-key-type', location,
|
|
1676
|
-
|
|
1713
|
+
{
|
|
1714
|
+
name: pathSegment, type: type.type, id: edmType, '#': pathSegment ? 'std' : 'scalar',
|
|
1715
|
+
});
|
|
1677
1716
|
}
|
|
1678
1717
|
}
|
|
1679
1718
|
}
|
|
@@ -1707,26 +1746,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1707
1746
|
Path="items/subitems/subitems/up_" Target="Header/items/subitems"/>
|
|
1708
1747
|
Path="items/subitems/subitems/toG" Target="G"/>
|
|
1709
1748
|
*/
|
|
1710
|
-
function initEdmNavPropBindingTargets(def) {
|
|
1711
|
-
if(def.$hasEntitySet) {
|
|
1749
|
+
function initEdmNavPropBindingTargets( def ) {
|
|
1750
|
+
if (def.$hasEntitySet) {
|
|
1712
1751
|
forEachGeneric(def.items || def, 'elements', (element) => {
|
|
1713
|
-
produceTargetPath([edmUtils.getBaseName(def.name)], element, def);
|
|
1752
|
+
produceTargetPath([ edmUtils.getBaseName(def.name) ], element, def);
|
|
1714
1753
|
});
|
|
1715
1754
|
}
|
|
1716
1755
|
|
|
1717
|
-
function produceTargetPath(prefix, elt, curDef) {
|
|
1718
|
-
const newPrefix = [...prefix, elt.name];
|
|
1719
|
-
if(isEdmPropertyRendered(elt, options)) {
|
|
1756
|
+
function produceTargetPath( prefix, elt, curDef ) {
|
|
1757
|
+
const newPrefix = [ ...prefix, elt.name ];
|
|
1758
|
+
if (isEdmPropertyRendered(elt, options)) {
|
|
1720
1759
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1721
1760
|
// unroll derived type chains for assocs
|
|
1722
|
-
if(elt.target && !elt.$visited) {
|
|
1723
|
-
if(!elt._target.$edmTgtPaths)
|
|
1761
|
+
if (elt.target && !elt.$visited) {
|
|
1762
|
+
if (!elt._target.$edmTgtPaths)
|
|
1724
1763
|
setProp(elt._target, '$edmTgtPaths', []);
|
|
1725
1764
|
// drill into target only if
|
|
1726
1765
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1727
1766
|
// 2) current definition and target are the same (cycle)
|
|
1728
1767
|
// 3) it's no external reference
|
|
1729
|
-
if(!elt.$externalRef &&
|
|
1768
|
+
if (!elt.$externalRef &&
|
|
1730
1769
|
!elt._target.$hasEntitySet &&
|
|
1731
1770
|
!elt._isToContainer &&
|
|
1732
1771
|
curDef !== elt._target) {
|
|
@@ -1739,10 +1778,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1739
1778
|
}
|
|
1740
1779
|
else {
|
|
1741
1780
|
// try to find elements to drill down further
|
|
1742
|
-
while(elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1781
|
+
while (elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1743
1782
|
elt = csn.definitions[elt.type];
|
|
1744
|
-
|
|
1745
|
-
if(elt && elt.elements && !elt.$visited) {
|
|
1783
|
+
|
|
1784
|
+
if (elt && elt.elements && !elt.$visited) {
|
|
1746
1785
|
setProp(elt, '$visited', true);
|
|
1747
1786
|
Object.values(elt.elements).forEach(e => produceTargetPath(newPrefix, e, curDef));
|
|
1748
1787
|
delete elt.$visited;
|
|
@@ -1752,8 +1791,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1752
1791
|
}
|
|
1753
1792
|
}
|
|
1754
1793
|
|
|
1755
|
-
function initEdmNavPropBindingPaths(def) {
|
|
1756
|
-
if(options.isV4() &&def.$hasEntitySet) {
|
|
1794
|
+
function initEdmNavPropBindingPaths( def ) {
|
|
1795
|
+
if (options.isV4() && def.$hasEntitySet) {
|
|
1757
1796
|
let npbs = [];
|
|
1758
1797
|
forEachGeneric(def.items || def, 'elements', (element) => {
|
|
1759
1798
|
npbs = npbs.concat(produceNavigationPath(element, def));
|
|
@@ -1762,50 +1801,52 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1762
1801
|
}
|
|
1763
1802
|
|
|
1764
1803
|
// collect all paths originating from this element that end up in an entity set
|
|
1765
|
-
function produceNavigationPath(elt, curDef) {
|
|
1804
|
+
function produceNavigationPath( elt, curDef ) {
|
|
1766
1805
|
let npbs = [];
|
|
1767
1806
|
const prefix = elt.name;
|
|
1768
|
-
if(isEdmPropertyRendered(elt, options)) {
|
|
1807
|
+
if (isEdmPropertyRendered(elt, options)) {
|
|
1769
1808
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1770
1809
|
// unroll derived type chains for assocs
|
|
1771
|
-
if(elt.target && !elt.$visited) {
|
|
1810
|
+
if (elt.target && !elt.$visited) {
|
|
1772
1811
|
// drill into target only if
|
|
1773
1812
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1774
1813
|
// 2) current definition and target are the same (cycle)
|
|
1775
1814
|
// 3) it's no external reference
|
|
1776
|
-
if(!elt.$externalRef &&
|
|
1815
|
+
if (!elt.$externalRef &&
|
|
1777
1816
|
!elt._target.$hasEntitySet &&
|
|
1778
1817
|
!elt._isToContainer &&
|
|
1779
1818
|
curDef !== elt._target) {
|
|
1780
1819
|
// follow elements in the target but avoid cycles
|
|
1781
1820
|
setProp(elt, '$visited', true);
|
|
1782
|
-
Object.values(elt._target.elements).forEach(e =>
|
|
1821
|
+
Object.values(elt._target.elements).forEach((e) => {
|
|
1822
|
+
npbs = npbs.concat(produceNavigationPath(e, elt._target));
|
|
1823
|
+
});
|
|
1783
1824
|
delete elt.$visited;
|
|
1784
1825
|
}
|
|
1785
|
-
else if(!(options.odataContainment && options.isV4() && elt['@odata.contained'])) {
|
|
1826
|
+
else if (!(options.odataContainment && options.isV4() && elt['@odata.contained'])) {
|
|
1786
1827
|
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
1787
1828
|
// last assoc step must not be to-n and target a singleton
|
|
1788
|
-
let
|
|
1829
|
+
let path;
|
|
1789
1830
|
if (!elt.$externalRef &&
|
|
1790
1831
|
!(edmUtils.isToMany(elt) &&
|
|
1791
1832
|
edmUtils.isSingleton(elt._target) &&
|
|
1792
1833
|
options.isV4())) {
|
|
1793
|
-
if(elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1794
|
-
|
|
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];
|
|
1795
1836
|
}
|
|
1796
|
-
else if(elt._target.$hasEntitySet) {
|
|
1837
|
+
else if (elt._target.$hasEntitySet) {
|
|
1797
1838
|
const baseName = edmUtils.getBaseName(elt._target.$entitySetName || elt._target.name);
|
|
1798
1839
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1799
1840
|
// if target is in another schema, target the full qualified entity set
|
|
1800
|
-
|
|
1801
|
-
[ baseName ] : [elt._target.$mySchemaName
|
|
1841
|
+
path = (elt._target.$mySchemaName === def.$mySchemaName)
|
|
1842
|
+
? [ baseName ] : [ `${elt._target.$mySchemaName}.EntityContainer`, baseName ];
|
|
1802
1843
|
}
|
|
1803
|
-
if(
|
|
1844
|
+
if (path) {
|
|
1804
1845
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1805
1846
|
// if target is in another schema, target the full qualified entity set
|
|
1806
1847
|
const npb = {
|
|
1807
1848
|
Path: elt.name,
|
|
1808
|
-
Target:
|
|
1849
|
+
Target: path.join('/'),
|
|
1809
1850
|
};
|
|
1810
1851
|
npbs.push( npb );
|
|
1811
1852
|
}
|
|
@@ -1816,61 +1857,65 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1816
1857
|
}
|
|
1817
1858
|
else {
|
|
1818
1859
|
// try to find elements to drill down further
|
|
1819
|
-
while(elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1860
|
+
while (elt && !(isBuiltinType(elt.type) || elt.elements))
|
|
1820
1861
|
elt = csn.definitions[elt.type];
|
|
1821
|
-
|
|
1822
|
-
if(elt && elt.elements && !elt.$visited) {
|
|
1862
|
+
|
|
1863
|
+
if (elt && elt.elements && !elt.$visited) {
|
|
1823
1864
|
setProp(elt, '$visited', true);
|
|
1824
|
-
Object.values(elt.elements).forEach(e =>
|
|
1865
|
+
Object.values(elt.elements).forEach((e) => {
|
|
1866
|
+
npbs = npbs.concat(produceNavigationPath(e, curDef));
|
|
1867
|
+
});
|
|
1825
1868
|
delete elt.$visited;
|
|
1826
1869
|
}
|
|
1827
1870
|
}
|
|
1828
1871
|
}
|
|
1829
|
-
npbs.forEach(p =>
|
|
1872
|
+
npbs.forEach((p) => {
|
|
1873
|
+
p.Path = `${prefix}/${p.Path}`;
|
|
1874
|
+
});
|
|
1830
1875
|
return npbs;
|
|
1831
1876
|
}
|
|
1832
1877
|
}
|
|
1833
1878
|
|
|
1834
|
-
function determineEntitySet(def) {
|
|
1879
|
+
function determineEntitySet( def ) {
|
|
1835
1880
|
// if this is an entity or a view, determine if an entity set is required or not
|
|
1836
1881
|
// 1) must not be a proxy and not a containee in V4
|
|
1837
1882
|
// No annos are rendered for non-existing EntitySet targets.
|
|
1838
|
-
if(def.$hasEntitySet === undefined) {
|
|
1883
|
+
if (def.$hasEntitySet === undefined) {
|
|
1839
1884
|
const hasEntitySet = def.kind === 'entity' && !(options.isV4() && edmUtils.isContainee(def)) && !def.$proxy;
|
|
1840
1885
|
setProp(def, '$hasEntitySet', hasEntitySet);
|
|
1841
1886
|
}
|
|
1842
1887
|
}
|
|
1843
1888
|
|
|
1844
|
-
function finalize(def, defName) {
|
|
1889
|
+
function finalize( def, defName ) {
|
|
1845
1890
|
// 1. let all doc props become @Core.Descriptions
|
|
1846
1891
|
// 2. mark a member that will become a collection
|
|
1847
1892
|
// 3. assign the edm primitive type to elements, to be used in the rendering later
|
|
1848
1893
|
// 4. assign @Validation.AllowedValues to enums
|
|
1849
|
-
const
|
|
1894
|
+
const defLocation = [ 'definitions', defName ];
|
|
1850
1895
|
edmUtils.assignAnnotation(def, '@Core.Description', def.doc);
|
|
1851
1896
|
markCollection(def);
|
|
1852
1897
|
mapCdsToEdmProp(def);
|
|
1853
|
-
annotateAllowedValues(def,
|
|
1854
|
-
if (def.returns)
|
|
1898
|
+
annotateAllowedValues(def, defLocation);
|
|
1899
|
+
if (def.returns) {
|
|
1855
1900
|
markCollection(def.returns);
|
|
1856
1901
|
mapCdsToEdmProp(def.returns);
|
|
1857
|
-
annotateAllowedValues(def.returns, [...
|
|
1902
|
+
annotateAllowedValues(def.returns, [ ...defLocation, 'returns' ]);
|
|
1858
1903
|
}
|
|
1859
|
-
forEachMemberRecursively(def, (member, _memberName, _prop,
|
|
1904
|
+
forEachMemberRecursively(def, (member, _memberName, _prop, location) => {
|
|
1860
1905
|
edmUtils.assignAnnotation(member, '@Core.Description', member.doc);
|
|
1861
1906
|
markCollection(member);
|
|
1862
1907
|
mapCdsToEdmProp(member);
|
|
1863
|
-
annotateAllowedValues(member,
|
|
1908
|
+
annotateAllowedValues(member, location);
|
|
1864
1909
|
ComputedDefaultValue(member);
|
|
1865
1910
|
if (member.returns) {
|
|
1866
1911
|
edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
|
|
1867
1912
|
markCollection(member.returns);
|
|
1868
1913
|
mapCdsToEdmProp(member.returns);
|
|
1869
|
-
annotateAllowedValues(member.returns, [...
|
|
1914
|
+
annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
|
|
1870
1915
|
}
|
|
1871
|
-
},
|
|
1916
|
+
}, defLocation);
|
|
1872
1917
|
// mark members that need to be rendered as collections
|
|
1873
|
-
function markCollection(obj) {
|
|
1918
|
+
function markCollection( obj ) {
|
|
1874
1919
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
1875
1920
|
if (items) {
|
|
1876
1921
|
edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : true);
|
|
@@ -1885,48 +1930,46 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1885
1930
|
- has no value but the base type is cds.String, use the
|
|
1886
1931
|
symbol as value
|
|
1887
1932
|
*/
|
|
1888
|
-
function annotateAllowedValues(node,
|
|
1933
|
+
function annotateAllowedValues( node, location ) {
|
|
1889
1934
|
let typeDef = node;
|
|
1890
|
-
if(!node.enum && node.type && !isBuiltinType(node.type))
|
|
1935
|
+
if (!node.enum && node.type && !isBuiltinType(node.type))
|
|
1891
1936
|
typeDef = csn.definitions[node.type];
|
|
1892
|
-
if(typeDef?.enum) {
|
|
1937
|
+
if (typeDef?.enum) {
|
|
1893
1938
|
const enumValue = [];
|
|
1894
|
-
for(const enumSymbol in typeDef.enum) {
|
|
1939
|
+
for (const enumSymbol in typeDef.enum) {
|
|
1895
1940
|
const result = { '@Core.SymbolicName': enumSymbol };
|
|
1896
1941
|
let enumSymbolDef = typeDef.enum[enumSymbol];
|
|
1897
|
-
while(enumSymbolDef && !enumSymbolDef.$visited && enumSymbolDef['#']) {
|
|
1942
|
+
while (enumSymbolDef && !enumSymbolDef.$visited && enumSymbolDef['#']) {
|
|
1898
1943
|
setProp(enumSymbolDef, '$visited', true);
|
|
1899
1944
|
enumSymbolDef = typeDef.enum[enumSymbolDef['#']];
|
|
1900
1945
|
}
|
|
1901
1946
|
// reset visited
|
|
1902
|
-
for(const es in typeDef.enum)
|
|
1947
|
+
for (const es in typeDef.enum)
|
|
1903
1948
|
delete typeDef.enum[es].$visited;
|
|
1904
1949
|
|
|
1905
|
-
if(enumSymbolDef) {
|
|
1906
|
-
if(enumSymbolDef.val !== undefined) {
|
|
1950
|
+
if (enumSymbolDef) {
|
|
1951
|
+
if (enumSymbolDef.val !== undefined) {
|
|
1907
1952
|
// 'null' value is represented spec conform as empty record in AllowedValues collection
|
|
1908
1953
|
result.Value = enumSymbolDef.val;
|
|
1909
1954
|
enumValue.push(result);
|
|
1910
1955
|
}
|
|
1911
|
-
else {
|
|
1912
|
-
if(typeDef.type === 'cds.String') {
|
|
1956
|
+
else if (typeDef.type === 'cds.String') {
|
|
1913
1957
|
// the symbol is used as value for type 'cds.String'
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
}
|
|
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)');
|
|
1923
1966
|
}
|
|
1924
1967
|
}
|
|
1925
1968
|
else { // enumSymbolDef not found
|
|
1926
1969
|
// omit the entry and warn
|
|
1927
|
-
warning('odata-enum-missing-value',
|
|
1928
|
-
|
|
1929
|
-
|
|
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)');
|
|
1930
1973
|
}
|
|
1931
1974
|
|
|
1932
1975
|
// Can't rely that @description has already been renamed to @Core.Description
|
|
@@ -1936,11 +1979,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1936
1979
|
if (desc)
|
|
1937
1980
|
result['@Core.Description'] = desc;
|
|
1938
1981
|
}
|
|
1939
|
-
if(enumValue.length > 0)
|
|
1982
|
+
if (enumValue.length > 0)
|
|
1940
1983
|
edmUtils.assignAnnotation(node, '@Validation.AllowedValues', enumValue);
|
|
1941
1984
|
}
|
|
1942
1985
|
}
|
|
1943
|
-
|
|
1944
1986
|
}
|
|
1945
1987
|
|
|
1946
1988
|
// If containment in V4 is active, annotations that would be assigned to the containees
|
|
@@ -1948,48 +1990,47 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1948
1990
|
// the containment navigation property.
|
|
1949
1991
|
// Today only Capabilities.*Restrictions are known to be remapped as there exists a CDS
|
|
1950
1992
|
// short cut annotation @readonly that gets expanded and can be safely remapped.
|
|
1951
|
-
function pullupCapabilitiesAnnotations(rootContainer) {
|
|
1952
|
-
|
|
1953
|
-
if(!options.odataCapabilitiesPullup)
|
|
1993
|
+
function pullupCapabilitiesAnnotations( rootContainer ) {
|
|
1994
|
+
if (!options.odataCapabilitiesPullup)
|
|
1954
1995
|
return;
|
|
1955
1996
|
// @Capabilities is applicable to EntitySet/Collection only
|
|
1956
|
-
if(!rootContainer.$hasEntitySet)
|
|
1997
|
+
if (!rootContainer.$hasEntitySet)
|
|
1957
1998
|
return;
|
|
1958
1999
|
|
|
1959
|
-
const isRecursiveContainment
|
|
1960
|
-
!!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
2000
|
+
const isRecursiveContainment
|
|
2001
|
+
= !!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
1961
2002
|
rootContainer.$containerNames.length === 1 &&
|
|
1962
2003
|
rootContainer.$containeeAssociations.some(entry => rootContainer.$containerNames.includes(entry.assoc.target)));
|
|
1963
2004
|
|
|
1964
2005
|
// Root nodes are not contained
|
|
1965
|
-
const isRootNode
|
|
1966
|
-
!!(!rootContainer.$containerNames ||
|
|
2006
|
+
const isRootNode
|
|
2007
|
+
= !!(!rootContainer.$containerNames ||
|
|
1967
2008
|
rootContainer.$containerNames && rootContainer.$containerNames.length === 0);
|
|
1968
2009
|
|
|
1969
|
-
if(!isRecursiveContainment && !isRootNode)
|
|
2010
|
+
if (!isRecursiveContainment && !isRootNode)
|
|
1970
2011
|
return;
|
|
1971
2012
|
|
|
1972
2013
|
const rootRestrictions = [];
|
|
1973
2014
|
addContainmentAnnotationsRecursively([], rootContainer);
|
|
1974
|
-
if(rootRestrictions.length)
|
|
2015
|
+
if (rootRestrictions.length)
|
|
1975
2016
|
rootContainer[NavResAnno] = rootRestrictions;
|
|
1976
2017
|
|
|
1977
|
-
function addContainmentAnnotationsRecursively(prefix, container) {
|
|
1978
|
-
if(container.$containeeAssociations) {
|
|
2018
|
+
function addContainmentAnnotationsRecursively( prefix, container ) {
|
|
2019
|
+
if (container.$containeeAssociations) {
|
|
1979
2020
|
// copy or create container restrictions, don't modify original
|
|
1980
|
-
const localRestrictions = container[NavResAnno]
|
|
1981
|
-
cloneAnnotationValue(container[NavResAnno]) : []
|
|
2021
|
+
const localRestrictions = container[NavResAnno]
|
|
2022
|
+
? cloneAnnotationValue(container[NavResAnno]) : [];
|
|
1982
2023
|
|
|
1983
2024
|
// prefix the existing navigation property restrictions on the container
|
|
1984
|
-
if(prefix.length) {
|
|
1985
|
-
localRestrictions.forEach(npe => {
|
|
1986
|
-
if(npe.NavigationProperty &&
|
|
2025
|
+
if (prefix.length) {
|
|
2026
|
+
localRestrictions.forEach((npe) => {
|
|
2027
|
+
if (npe.NavigationProperty &&
|
|
1987
2028
|
npe.NavigationProperty['='] &&
|
|
1988
2029
|
typeof npe.NavigationProperty['='] === 'string') {
|
|
1989
|
-
applyTransformations({ definitions: { npe }}, {
|
|
2030
|
+
applyTransformations({ definitions: { npe } }, {
|
|
1990
2031
|
'=': (parent, prop, value) => {
|
|
1991
2032
|
parent[prop] = prefix.concat(value).join('.');
|
|
1992
|
-
}
|
|
2033
|
+
},
|
|
1993
2034
|
});
|
|
1994
2035
|
}
|
|
1995
2036
|
});
|
|
@@ -1997,34 +2038,32 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1997
2038
|
|
|
1998
2039
|
setProp(container, '$visited', true);
|
|
1999
2040
|
// collect capabilities from containees
|
|
2000
|
-
container.$containeeAssociations.forEach(entry => {
|
|
2041
|
+
container.$containeeAssociations.forEach((entry) => {
|
|
2001
2042
|
const { assoc, path } = entry;
|
|
2002
2043
|
const containee = assoc._target;
|
|
2003
2044
|
|
|
2004
|
-
if(edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
2045
|
+
if (edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
2005
2046
|
const localAssocPath = path.join('.');
|
|
2006
|
-
let navPropEntry= localRestrictions.find(p =>
|
|
2007
|
-
p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
|
|
2047
|
+
let navPropEntry = localRestrictions.find(p => p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
|
|
2008
2048
|
const hasEntry = !!navPropEntry;
|
|
2009
2049
|
|
|
2010
|
-
if(!hasEntry)
|
|
2011
|
-
navPropEntry =
|
|
2012
|
-
|
|
2050
|
+
if (!hasEntry)
|
|
2051
|
+
navPropEntry = { NavigationProperty: { '=': prefix.concat(localAssocPath).join('.') } };
|
|
2052
|
+
|
|
2013
2053
|
|
|
2014
2054
|
const props = Object.entries(containee);
|
|
2015
2055
|
let newEntry = false;
|
|
2016
|
-
capabilities.forEach(c => {
|
|
2017
|
-
if(edmUtils.mergeIntoNavPropEntry(c, navPropEntry, prefix.concat(path), props))
|
|
2056
|
+
capabilities.forEach((c) => {
|
|
2057
|
+
if (edmUtils.mergeIntoNavPropEntry(c, navPropEntry, prefix.concat(path), props))
|
|
2018
2058
|
newEntry = true;
|
|
2019
2059
|
});
|
|
2020
2060
|
|
|
2021
|
-
if(newEntry && !hasEntry)
|
|
2061
|
+
if (newEntry && !hasEntry)
|
|
2022
2062
|
localRestrictions.push(navPropEntry);
|
|
2023
|
-
}
|
|
2024
2063
|
|
|
2025
|
-
|
|
2064
|
+
|
|
2065
|
+
if (!containee.$visited)
|
|
2026
2066
|
addContainmentAnnotationsRecursively(prefix.concat(path), containee);
|
|
2027
|
-
}
|
|
2028
2067
|
}
|
|
2029
2068
|
});
|
|
2030
2069
|
|
|
@@ -2045,95 +2084,97 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
2045
2084
|
parameter, a warning is raised, Core.OptionalParameter requires that all optional
|
|
2046
2085
|
parameters appear rightmost.
|
|
2047
2086
|
*/
|
|
2048
|
-
function annotateOptionalActFuncParams(def, defName) {
|
|
2049
|
-
if(!isBetaEnabled(options, 'optionalActionFunctionParameters'))
|
|
2087
|
+
function annotateOptionalActFuncParams( def, defName ) {
|
|
2088
|
+
if (!isBetaEnabled(options, 'optionalActionFunctionParameters'))
|
|
2050
2089
|
return;
|
|
2051
2090
|
// return if there is nothing to do
|
|
2052
|
-
const loc = [ 'definitions', defName ]
|
|
2053
|
-
if(def.kind === 'function' || def.kind === 'action')
|
|
2091
|
+
const loc = [ 'definitions', defName ];
|
|
2092
|
+
if (def.kind === 'function' || def.kind === 'action')
|
|
2054
2093
|
iterateParams(def, loc.concat('params'));
|
|
2055
|
-
if(def.actions) {
|
|
2056
|
-
for(const an in def.actions) {
|
|
2094
|
+
if (def.actions) {
|
|
2095
|
+
for (const an in def.actions) {
|
|
2057
2096
|
const a = def.actions[an];
|
|
2058
|
-
iterateParams(a, loc.concat(['actions', an, 'params' ]));
|
|
2097
|
+
iterateParams(a, loc.concat([ 'actions', an, 'params' ]));
|
|
2059
2098
|
}
|
|
2060
2099
|
}
|
|
2061
2100
|
|
|
2062
|
-
function iterateParams(
|
|
2101
|
+
function iterateParams( action, location ) {
|
|
2063
2102
|
let optPns = [];
|
|
2064
|
-
|
|
2103
|
+
if (action.params) {
|
|
2104
|
+
Object.values(action.params).forEach((p) => {
|
|
2065
2105
|
// user assigned annotation, don't touch it
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
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) {
|
|
2074
2119
|
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
else if(!p.notNull) {
|
|
2079
|
-
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2080
|
-
optPns.push(p);
|
|
2081
|
-
|
|
2082
|
-
}
|
|
2083
|
-
else {
|
|
2120
|
+
optPns.push(p);
|
|
2121
|
+
}
|
|
2122
|
+
else {
|
|
2084
2123
|
// this is a mandatory parameter, warn about all previously collected optional parameters
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
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
|
+
}
|
|
2093
2133
|
}
|
|
2094
2134
|
}
|
|
2095
2135
|
|
|
2096
|
-
|
|
2136
|
+
// ////////////////////////////////////////////////////////////////////
|
|
2097
2137
|
//
|
|
2098
2138
|
// Helper section starts here
|
|
2099
2139
|
//
|
|
2100
2140
|
|
|
2101
2141
|
|
|
2102
|
-
function mapCdsToEdmProp(obj) {
|
|
2142
|
+
function mapCdsToEdmProp( obj ) {
|
|
2103
2143
|
if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
|
|
2104
|
-
|
|
2144
|
+
const edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
2105
2145
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2106
|
-
}
|
|
2107
|
-
|
|
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);
|
|
2108
2149
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2109
2150
|
}
|
|
2110
2151
|
// This is the special case when we have array of array, but will not be supported in the future
|
|
2111
2152
|
else if (obj._isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(csnUtils.getFinalTypeInfo(obj.items.items.type)?.type)) {
|
|
2112
|
-
|
|
2153
|
+
const edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
2113
2154
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2114
2155
|
}
|
|
2115
2156
|
}
|
|
2116
2157
|
|
|
2117
|
-
function ComputedDefaultValue(member) {
|
|
2158
|
+
function ComputedDefaultValue( member ) {
|
|
2118
2159
|
if (member.default && !csn['@Core.ComputedDefaultValue']) {
|
|
2119
2160
|
let def = member.default;
|
|
2120
2161
|
let noTailExpr = false;
|
|
2121
|
-
if(def.xpr) {
|
|
2162
|
+
if (def.xpr) {
|
|
2122
2163
|
let i = 0;
|
|
2123
2164
|
// consume all unary signs
|
|
2124
|
-
while(def.xpr[i] === '-' || def.xpr[i] === '+')
|
|
2165
|
+
while (def.xpr[i] === '-' || def.xpr[i] === '+')
|
|
2166
|
+
i++;
|
|
2125
2167
|
// noTailExpr is true if there is nothing behind the next token in the stream
|
|
2126
|
-
noTailExpr = i < def.xpr.length-1;
|
|
2168
|
+
noTailExpr = i < def.xpr.length - 1;
|
|
2127
2169
|
def = def.xpr[i];
|
|
2128
2170
|
}
|
|
2129
2171
|
// it is a computed value if it is not a simple value or an annotation
|
|
2130
|
-
if(!((def.val !== undefined && !noTailExpr) || def['#']))
|
|
2172
|
+
if (!((def.val !== undefined && !noTailExpr) || def['#']))
|
|
2131
2173
|
edmUtils.assignAnnotation(member, '@Core.ComputedDefaultValue', true);
|
|
2132
|
-
}
|
|
2133
2174
|
}
|
|
2134
2175
|
}
|
|
2135
2176
|
}
|
|
2136
2177
|
|
|
2137
2178
|
module.exports = {
|
|
2138
2179
|
initializeModel,
|
|
2139
|
-
}
|
|
2180
|
+
};
|