@sap/cds-compiler 2.13.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/* eslint max-statements-per-line:off */
|
|
3
3
|
const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
4
4
|
const { forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
5
|
-
isEdmPropertyRendered, getUtils,
|
|
5
|
+
isEdmPropertyRendered, getUtils, cloneCsnNonDict, isBuiltinType } = require('../model/csnUtils');
|
|
6
6
|
const edmUtils = require('./edmUtils.js');
|
|
7
7
|
const typesExposure = require('../transform/odata/typesExposure');
|
|
8
8
|
const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
|
|
@@ -18,10 +18,8 @@ const {
|
|
|
18
18
|
isParameterizedEntity,
|
|
19
19
|
resolveOnConditionAndPrepareConstraints,
|
|
20
20
|
finalizeReferentialConstraints,
|
|
21
|
-
isODataSimpleIdentifier,
|
|
22
21
|
isEntity,
|
|
23
22
|
getSchemaPrefix,
|
|
24
|
-
isActionOrFunction
|
|
25
23
|
} = require('./edmUtils.js');
|
|
26
24
|
|
|
27
25
|
/**
|
|
@@ -34,62 +32,46 @@ const {
|
|
|
34
32
|
* @param {CSN.Model} csn
|
|
35
33
|
* @param {object} _options
|
|
36
34
|
*/
|
|
37
|
-
function initializeModel(csn, _options, messageFunctions)
|
|
35
|
+
function initializeModel(csn, _options, messageFunctions, requestedServiceNames=undefined)
|
|
38
36
|
{
|
|
39
|
-
|
|
40
|
-
throw Error('Please debug me: initializeModel must be invoked with options');
|
|
37
|
+
const { info, warning, error, message, throwWithAnyError } = messageFunctions;
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const csnUtils = getUtils(csn);
|
|
45
|
-
const {
|
|
46
|
-
inspectRef,
|
|
47
|
-
getCsnDef,
|
|
48
|
-
getFinalTypeDef,
|
|
49
|
-
isStructured,
|
|
50
|
-
isAssocOrComposition,
|
|
51
|
-
} = getUtils(csn);
|
|
39
|
+
let csnUtils = getUtils(csn);
|
|
52
40
|
|
|
53
41
|
// proxies are merged into the final model after all proxy elements are collected
|
|
54
42
|
const proxyCache = [];
|
|
43
|
+
// iterarte only over those definitions that need to be preprocessed
|
|
44
|
+
// instead of mangling through the whole model each time
|
|
45
|
+
// preprocess steps removing adding to the model must co-modify this map
|
|
46
|
+
const reqDefs = { definitions: Object.create(null) };
|
|
47
|
+
|
|
55
48
|
|
|
56
49
|
// make sure options are complete
|
|
57
50
|
let options = validateOptions(_options);
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if(art.kind === 'service') {
|
|
64
|
-
serviceRoots[artName] = Object.assign(art, { name: artName });
|
|
65
|
-
}
|
|
66
|
-
return serviceRoots;
|
|
67
|
-
}, Object.create(null) );
|
|
52
|
+
const [ serviceRoots,
|
|
53
|
+
serviceRootNames,
|
|
54
|
+
fallBackSchemaName,
|
|
55
|
+
whatsMyServiceRootName ] = getAnOverviewOnTheServices(csn);
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
function whatsMyServiceRootName(n, self=true) {
|
|
73
|
-
return serviceRootNames.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') || (n === sn && self) ? sn : rc, undefined);
|
|
57
|
+
if(serviceRootNames.length === 0) {
|
|
58
|
+
return [serviceRoots, Object.create(null), reqDefs, whatsMyServiceRootName, fallBackSchemaName, options];
|
|
74
59
|
}
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
while (allDefs.some(([artName, _art]) => {
|
|
81
|
-
const p = artName.split('.');
|
|
82
|
-
return p.length === 2 && p[0] === autoexposeSchemaName;
|
|
83
|
-
})) {
|
|
84
|
-
autoexposeSchemaName = 'root' + i++;
|
|
61
|
+
if(requestedServiceNames === undefined)
|
|
62
|
+
requestedServiceNames = options.serviceNames;
|
|
63
|
+
if(requestedServiceNames === undefined) {
|
|
64
|
+
requestedServiceNames = serviceRootNames;
|
|
85
65
|
}
|
|
86
66
|
|
|
87
|
-
|
|
88
|
-
return
|
|
67
|
+
function isMyServiceRequested(n) {
|
|
68
|
+
return requestedServiceNames.includes(whatsMyServiceRootName(n));
|
|
89
69
|
}
|
|
90
70
|
|
|
91
71
|
// Structural CSN inbound QA checks
|
|
92
72
|
inboundQualificationChecks();
|
|
73
|
+
// not needed at the moment
|
|
74
|
+
// resolveForeignKeyRefs();
|
|
93
75
|
|
|
94
76
|
if(isBetaEnabled(options, undefined)) {
|
|
95
77
|
splitDottedDefinitionsIntoSeparateServices();
|
|
@@ -102,10 +84,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
102
84
|
renameDottedDefinitionsInsideServiceOrContext();
|
|
103
85
|
|
|
104
86
|
/*
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
87
|
+
Final base type expansion is required here when:
|
|
88
|
+
1) The input CSN was already transformed for V4 but shall be rendered in V2 and the
|
|
89
|
+
edmx generator is called directly (bypassing OData transformation)
|
|
90
|
+
2) The input CSN was already transformed for V4 and persisted (all non-enumerables are
|
|
91
|
+
stripped of)
|
|
92
|
+
3) call via cdsc
|
|
93
|
+
|
|
109
94
|
At the end of the day, this module must be called only here, in the renderer and removed
|
|
110
95
|
as a step in the OData transformer with the goal to have a protocol agnostic OData CSN.
|
|
111
96
|
*/
|
|
@@ -113,20 +98,18 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
113
98
|
const { toFinalBaseType }= require('../transform/transformUtilsNew').getTransformers(csn, options);
|
|
114
99
|
expandCSNToFinalBaseType(csn, { toFinalBaseType }, csnUtils, serviceRootNames, options);
|
|
115
100
|
}
|
|
101
|
+
|
|
116
102
|
/*
|
|
117
|
-
Enrich the CSN by de-anonymizing and exposing
|
|
103
|
+
Enrich the CSN by de-anonymizing and exposing types that are required to make the service self contained.
|
|
118
104
|
Type exposure will add additional schema contexts and group the exposed types in these contexts.
|
|
119
105
|
contexts either represent another service (if the type to be exposed resides in that
|
|
120
106
|
service), the namespace (including (sub-)contexts) or as last resort (if the type name
|
|
121
107
|
has no prefix path) a 'root' namespace.
|
|
122
108
|
*/
|
|
123
|
-
const schemas = typesExposure(csn, whatsMyServiceRootName,
|
|
124
|
-
|
|
125
|
-
// First attach names to all definitions (and actions/params) in the model
|
|
126
|
-
// elements are done in initializeStruct
|
|
127
|
-
forEachDefinition(csn, attachNameProperty);
|
|
109
|
+
const schemas = typesExposure(csn, whatsMyServiceRootName, requestedServiceNames,
|
|
110
|
+
fallBackSchemaName, options, csnUtils, { error });
|
|
128
111
|
|
|
129
|
-
//
|
|
112
|
+
// Get an overview about all schemas (including the services)
|
|
130
113
|
const schemaNames = [...serviceRootNames];
|
|
131
114
|
schemaNames.push(...Object.keys(schemas));
|
|
132
115
|
// sort schemas in reverse order to allow longest match in whatsMySchemaName function
|
|
@@ -136,53 +119,94 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
136
119
|
}
|
|
137
120
|
|
|
138
121
|
if(schemaNames.length) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
122
|
+
forEachDefinition(csn, [
|
|
123
|
+
attachNameProperty,
|
|
124
|
+
(def, defName) => {
|
|
125
|
+
const mySchemaName = whatsMySchemaName(defName);
|
|
126
|
+
mySchemaName && setProp(def, '$mySchemaName', mySchemaName);
|
|
127
|
+
if(isMyServiceRequested(defName))
|
|
128
|
+
reqDefs.definitions[defName] = def;
|
|
129
|
+
},
|
|
130
|
+
linkAssociationTarget ]);
|
|
131
|
+
// initialize requested services
|
|
132
|
+
const skip = { skipArtifact: (_def, defName) => !isMyServiceRequested(defName) };
|
|
133
|
+
forEachDefinition({ definitions: serviceRoots }, initService, skip);
|
|
146
134
|
// Create data structures for containments
|
|
147
|
-
forEachDefinition(
|
|
135
|
+
forEachDefinition(reqDefs, initContainments);
|
|
148
136
|
// Initialize entities with parameters (add Parameter entity)
|
|
149
|
-
forEachDefinition(
|
|
137
|
+
forEachDefinition(reqDefs, initParameterizedEntityOrView);
|
|
150
138
|
// Initialize structures
|
|
151
|
-
forEachDefinition(csn,
|
|
139
|
+
forEachDefinition(csn, initStructure);
|
|
152
140
|
// Initialize associations after _parent linking
|
|
153
|
-
forEachDefinition(
|
|
141
|
+
forEachDefinition(reqDefs, initConstraints);
|
|
154
142
|
// Mute V4 elements depending on constraint preparation
|
|
155
143
|
if(options.isV4())
|
|
156
|
-
forEachDefinition(
|
|
144
|
+
forEachDefinition(reqDefs, ignoreProperties);
|
|
157
145
|
// calculate constraints based on ignoreProperties and prepareConstraints
|
|
158
|
-
forEachDefinition(
|
|
146
|
+
forEachDefinition(reqDefs, finalizeConstraints);
|
|
159
147
|
// convert exposed types into cross schema references if required
|
|
160
148
|
// must be run before proxy exposure to avoid potential reference collisions
|
|
161
149
|
convertExposedTypesOfOtherServicesIntoCrossReferences();
|
|
162
150
|
// create association target proxies
|
|
163
151
|
// Decide if an entity set needs to be constructed or not
|
|
164
|
-
forEachDefinition(
|
|
152
|
+
forEachDefinition(reqDefs, [ exposeTargetsAsProxiesOrSchemaRefs, determineEntitySet ]);
|
|
165
153
|
// finalize proxy creation
|
|
166
154
|
mergeProxiesIntoModel();
|
|
167
155
|
|
|
168
156
|
if(options.isV4())
|
|
169
|
-
forEachDefinition(
|
|
157
|
+
forEachDefinition(reqDefs, initEdmNavPropBindingTargets);
|
|
170
158
|
|
|
171
159
|
// Things that can be done in one pass
|
|
172
160
|
// Create edmKeyRefPaths
|
|
173
161
|
// Create NavigationPropertyBindings, requires determineEntitySet
|
|
174
162
|
// Map /** doc comments */ to @CoreDescription
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
163
|
+
forEachDefinition(reqDefs, [
|
|
164
|
+
initEdmKeyRefPaths,
|
|
165
|
+
initEdmNavPropBindingPaths,
|
|
166
|
+
initEdmTypesAndDescription
|
|
167
|
+
]);
|
|
178
168
|
}
|
|
179
|
-
return [serviceRoots, schemas, whatsMyServiceRootName,
|
|
169
|
+
return [serviceRoots, schemas, reqDefs, whatsMyServiceRootName, fallBackSchemaName, options];
|
|
180
170
|
|
|
181
171
|
//////////////////////////////////////////////////////////////////////
|
|
182
172
|
//
|
|
183
173
|
// Service initialization starts here
|
|
184
174
|
//
|
|
185
175
|
|
|
176
|
+
function getAnOverviewOnTheServices(csn) {
|
|
177
|
+
const defs = csn.definitions || {};
|
|
178
|
+
const serviceRoots = Object.create(null);
|
|
179
|
+
for(const defName in defs) {
|
|
180
|
+
const def = defs[defName];
|
|
181
|
+
if(def && def.kind === 'service')
|
|
182
|
+
serviceRoots[defName] = Object.assign(def, { name: defName });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// first of all we need to know about all 'real' user defined services
|
|
186
|
+
const serviceRootNames = Object.keys(serviceRoots).sort((a,b)=>b.length-a.length);
|
|
187
|
+
|
|
188
|
+
function whatsMyServiceRootName(n, self=true) {
|
|
189
|
+
return serviceRootNames.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') || (n === sn && self) ? sn : rc, undefined);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// find a globally unambiguous schema name to collect all top level 'root' types
|
|
193
|
+
// TODO: work on service basis (this requires post exposure renaming)
|
|
194
|
+
let fallBackSchemaName = 'root';
|
|
195
|
+
let i = 1;
|
|
196
|
+
while (Object.keys(defs).some(artName => {
|
|
197
|
+
const p = artName.split('.');
|
|
198
|
+
return p.length === 2 && p[0] === fallBackSchemaName;
|
|
199
|
+
})) {
|
|
200
|
+
fallBackSchemaName = 'root' + i++;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return [
|
|
204
|
+
serviceRoots,
|
|
205
|
+
serviceRootNames,
|
|
206
|
+
fallBackSchemaName,
|
|
207
|
+
whatsMyServiceRootName ];
|
|
208
|
+
}
|
|
209
|
+
|
|
186
210
|
/*
|
|
187
211
|
Replace dots in sub-service and sub-context definitions with underscores to be
|
|
188
212
|
Odata ID compliant.
|
|
@@ -194,9 +218,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
194
218
|
// Find the first definition above the current definition or undefined otherwise.
|
|
195
219
|
// Definition can either be a context or a service
|
|
196
220
|
function getRootDef(name) {
|
|
221
|
+
const scopeKinds = {'service':1, 'context':1};
|
|
197
222
|
let pos = name.lastIndexOf('.');
|
|
198
223
|
name = pos < 0 ? undefined : name.substring(0, pos);
|
|
199
|
-
while (name && !
|
|
224
|
+
while (name && !((csn.definitions[name] && csn.definitions[name].kind) in scopeKinds)) {
|
|
200
225
|
pos = name.lastIndexOf('.');
|
|
201
226
|
name = pos < 0 ? undefined : name.substring(0, pos);
|
|
202
227
|
}
|
|
@@ -205,8 +230,9 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
205
230
|
|
|
206
231
|
const dotEntityNameMap = Object.create(null);
|
|
207
232
|
const dotTypeNameMap = Object.create(null);
|
|
233
|
+
const kinds = {'entity':1, 'type':1, 'action':1, 'function':1};
|
|
208
234
|
forEachDefinition(csn, (def, defName) => {
|
|
209
|
-
if(
|
|
235
|
+
if(def.kind in kinds) {
|
|
210
236
|
const rootDef = getRootDef(defName);
|
|
211
237
|
// if this definition has a root def and the root def is not the service/schema name
|
|
212
238
|
// => service C { type D.E }, replace the prefix dots with underscores
|
|
@@ -276,7 +302,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
276
302
|
const myServiceRoot = whatsMyServiceRootName(defName);
|
|
277
303
|
const mySchemaPrefix = getSchemaPrefix(defName);
|
|
278
304
|
if(myServiceRoot && options.isV4() &&
|
|
279
|
-
/*(options.
|
|
305
|
+
/*(options.odataProxies || options.odataXServiceRefs) && options.isStructFormat && */
|
|
280
306
|
defName !== myServiceRoot && myServiceRoot !== mySchemaPrefix) {
|
|
281
307
|
const service = { kind: 'service', name: mySchemaPrefix };
|
|
282
308
|
serviceRoots[mySchemaPrefix] = service;
|
|
@@ -303,24 +329,14 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
303
329
|
}
|
|
304
330
|
|
|
305
331
|
// initialize the service itself
|
|
306
|
-
function
|
|
307
|
-
|
|
308
|
-
if (service.name.length > 511) {
|
|
309
|
-
error(null, ['definitions', service.name], 'OData namespace must not exceed 511 characters' );
|
|
310
|
-
}
|
|
311
|
-
const simpleIdentifiers = service.name.split('.');
|
|
312
|
-
simpleIdentifiers.forEach((identifier) => {
|
|
313
|
-
if (!isODataSimpleIdentifier(identifier)) {
|
|
314
|
-
signalIllegalIdentifier(identifier, ['definitions', service.name]);
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
setSAPSpecificV2AnnotationsToEntityContainer(options, service);
|
|
332
|
+
function initService(serviceRoot) {
|
|
333
|
+
setSAPSpecificV2AnnotationsToEntityContainer(options, serviceRoot);
|
|
318
334
|
}
|
|
319
335
|
|
|
320
336
|
// link association target to association and add @odata.contained to compositions in V4
|
|
321
337
|
function linkAssociationTarget(struct) {
|
|
322
338
|
forEachMemberRecursively(struct, (element, name, prop, subpath) => {
|
|
323
|
-
if(isAssociationOrComposition(element)
|
|
339
|
+
if(isAssociationOrComposition(element)) {
|
|
324
340
|
if(!element._target) {
|
|
325
341
|
let target = csn.definitions[element.target];
|
|
326
342
|
if(target) {
|
|
@@ -361,7 +377,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
361
377
|
// non-containment rendering. If containment rendering is active, the containee has no
|
|
362
378
|
// entity set. Instead try to rewrite the annotation in such a way that it is effective
|
|
363
379
|
// on the containment navigation property.
|
|
364
|
-
function
|
|
380
|
+
function initContainments(container) {
|
|
365
381
|
if(container.kind === 'entity') {
|
|
366
382
|
forEachMemberRecursively(container, initContainments,
|
|
367
383
|
[], true, { elementsOnly: true });
|
|
@@ -424,7 +440,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
424
440
|
// must be called.
|
|
425
441
|
// As a param entity is a potential proxy candidate, this split must be performed on
|
|
426
442
|
// all definitions
|
|
427
|
-
function
|
|
443
|
+
function initParameterizedEntityOrView(entityCsn, entityName) {
|
|
428
444
|
|
|
429
445
|
if(!isParameterizedEntity(entityCsn))
|
|
430
446
|
return;
|
|
@@ -441,13 +457,71 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
441
457
|
// Backlink Navigation Property "Parameters" to <ViewName>Parameters
|
|
442
458
|
|
|
443
459
|
// this code can be extended for aggregated views
|
|
444
|
-
const
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
const backlinkAssocName = 'Parameters';
|
|
460
|
+
const typeEntityName = entityName + 'Type';
|
|
461
|
+
const typeEntitySetName = entityName + 'Set';
|
|
462
|
+
const parameterToTypeAssocName = 'Set';
|
|
463
|
+
const typeToParameterAssocName = 'Parameters';
|
|
449
464
|
let hasBacklink = true;
|
|
450
465
|
|
|
466
|
+
|
|
467
|
+
// create the Parameter Definition
|
|
468
|
+
const parameterCsn = createParameterEntity(entityCsn, entityName, false);
|
|
469
|
+
|
|
470
|
+
// create the Type Definition
|
|
471
|
+
// modify the original parameter entity with backlink and new name
|
|
472
|
+
if(csn.definitions[typeEntityName])
|
|
473
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: typeEntityName });
|
|
474
|
+
else {
|
|
475
|
+
csn.definitions[typeEntityName] = entityCsn;
|
|
476
|
+
reqDefs.definitions[typeEntityName] = entityCsn;
|
|
477
|
+
delete csn.definitions[entityCsn.name];
|
|
478
|
+
delete reqDefs.definitions[entityCsn.name];
|
|
479
|
+
entityCsn.name = typeEntityName;
|
|
480
|
+
}
|
|
481
|
+
setProp(entityCsn, '$entitySetName', typeEntitySetName);
|
|
482
|
+
// add backlink association
|
|
483
|
+
if(hasBacklink) {
|
|
484
|
+
entityCsn.elements[typeToParameterAssocName] = {
|
|
485
|
+
name: typeToParameterAssocName,
|
|
486
|
+
target: parameterCsn.name,
|
|
487
|
+
type: 'cds.Association',
|
|
488
|
+
on: [ { ref: [ 'Parameters', 'Set' ] }, '=', { ref: [ '$self' ] } ]
|
|
489
|
+
};
|
|
490
|
+
setProp(entityCsn.elements[typeToParameterAssocName], '_selfReferences', []);
|
|
491
|
+
setProp(entityCsn.elements[typeToParameterAssocName], '_target', parameterCsn);
|
|
492
|
+
setProp(entityCsn.elements[typeToParameterAssocName], '$path',
|
|
493
|
+
[ 'definitions', typeEntityName, 'elements', typeToParameterAssocName ] );
|
|
494
|
+
|
|
495
|
+
// rewrite $path
|
|
496
|
+
if(entityCsn.$path)
|
|
497
|
+
entityCsn.$path[1] = typeEntityName;
|
|
498
|
+
forEachMemberRecursively(entityCsn, (member) => {
|
|
499
|
+
if(member.$path)
|
|
500
|
+
member.$path[1] = typeEntityName;
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/*
|
|
505
|
+
<EntitySet Name="ZRHA_TEST_CDSSet" EntityType="ZRHA_TEST_CDS_CDS.ZRHA_TEST_CDSType" sap:creatable="false" sap:updatable="false"
|
|
506
|
+
sap:deletable="false" sap:addressable="false" sap:content-version="1"/>
|
|
507
|
+
*/
|
|
508
|
+
assignProp(entityCsn, '_SetAttributes',
|
|
509
|
+
{'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.addressable': false });
|
|
510
|
+
|
|
511
|
+
// redirect inbound associations/compositions to the parameter entity
|
|
512
|
+
Object.keys(entityCsn.$sources || {}).forEach(n => {
|
|
513
|
+
// preserve the original target for constraint calculation
|
|
514
|
+
setProp(entityCsn.$sources[n], '_originalTarget', entityCsn.$sources[n]._target);
|
|
515
|
+
entityCsn.$sources[n]._target = parameterCsn;
|
|
516
|
+
entityCsn.$sources[n].target = parameterCsn.name;
|
|
517
|
+
});
|
|
518
|
+
rewriteContainmentAnnotations(parameterCsn, entityCsn, parameterToTypeAssocName);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function createParameterEntity(entityCsn, entityName, isProxy) {
|
|
522
|
+
const parameterEntityName = entityName + 'Parameters';
|
|
523
|
+
const parameterToTypeAssocName = 'Set';
|
|
524
|
+
|
|
451
525
|
// Construct the parameter entity
|
|
452
526
|
const parameterCsn = {
|
|
453
527
|
name: parameterEntityName,
|
|
@@ -455,8 +529,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
455
529
|
elements: Object.create(null),
|
|
456
530
|
'@sap.semantics': 'parameters',
|
|
457
531
|
};
|
|
458
|
-
|
|
459
|
-
|
|
532
|
+
if(!isProxy)
|
|
533
|
+
setProp(parameterCsn, '$entitySetName', entityName);
|
|
460
534
|
if(entityCsn.$location){
|
|
461
535
|
assignProp(parameterCsn, '$location', entityCsn.$location);
|
|
462
536
|
}
|
|
@@ -482,9 +556,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
482
556
|
entityCsn._containerEntity = [ parameterCsn ];
|
|
483
557
|
|
|
484
558
|
forEachGeneric(entityCsn, 'params', (p,n) => {
|
|
485
|
-
let elt =
|
|
559
|
+
let elt = cloneCsnNonDict(p, options);
|
|
486
560
|
elt.name = n;
|
|
487
561
|
delete elt.kind;
|
|
562
|
+
setProp(elt, '$path', [ 'definitions', parameterEntityName, 'elements', n ]);
|
|
488
563
|
elt.key = true; // params become primary key in parameter entity
|
|
489
564
|
/*
|
|
490
565
|
Spec meeting decision 28.02.22:
|
|
@@ -500,81 +575,35 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
500
575
|
parameterCsn.elements[n] = elt;
|
|
501
576
|
});
|
|
502
577
|
linkAssociationTarget(parameterCsn);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
cardinality: { src: 1, min: 0, max: '*' }
|
|
511
|
-
};
|
|
512
|
-
setProp(parameterCsn.elements[parameterToOriginalAssocName], '_target', entityCsn);
|
|
513
|
-
setProp(parameterCsn.elements[parameterToOriginalAssocName], '$path',
|
|
514
|
-
[ 'definitions', parameterEntityName, 'elements', parameterToOriginalAssocName ] );
|
|
515
|
-
|
|
516
|
-
// rewrite $path
|
|
517
|
-
setProp(parameterCsn, '$path', [ 'definitions', parameterEntityName ]);
|
|
518
|
-
forEachMemberRecursively(parameterCsn, (member) => {
|
|
519
|
-
if(member.$path)
|
|
520
|
-
member.$path[1] = parameterEntityName;
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
if(csn.definitions[parameterCsn.name])
|
|
524
|
-
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
|
|
525
|
-
else
|
|
526
|
-
csn.definitions[parameterCsn.name] = parameterCsn;
|
|
527
|
-
// modify the original parameter entity with backlink and new name
|
|
528
|
-
if(csn.definitions[originalEntityName])
|
|
529
|
-
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: originalEntityName });
|
|
530
|
-
else {
|
|
531
|
-
csn.definitions[originalEntityName] = entityCsn;
|
|
532
|
-
delete csn.definitions[entityCsn.name];
|
|
533
|
-
entityCsn.name = originalEntityName;
|
|
534
|
-
}
|
|
535
|
-
setProp(entityCsn, '$entitySetName', originalEntitySetName);
|
|
536
|
-
// add backlink association
|
|
537
|
-
if(hasBacklink) {
|
|
538
|
-
entityCsn.elements[backlinkAssocName] = {
|
|
539
|
-
name: backlinkAssocName,
|
|
540
|
-
target: parameterCsn.name,
|
|
578
|
+
initContainments(parameterCsn);
|
|
579
|
+
// add assoc to result set, FIXME: is the cardinality correct?
|
|
580
|
+
if(!isProxy) {
|
|
581
|
+
parameterCsn.elements[parameterToTypeAssocName] = {
|
|
582
|
+
'@odata.contained': true,
|
|
583
|
+
name: parameterToTypeAssocName,
|
|
584
|
+
target: entityCsn.name,
|
|
541
585
|
type: 'cds.Association',
|
|
542
|
-
|
|
586
|
+
cardinality: { src: 1, min: 0, max: '*' }
|
|
543
587
|
};
|
|
544
|
-
setProp(
|
|
545
|
-
setProp(
|
|
546
|
-
|
|
547
|
-
[ 'definitions', originalEntityName, 'elements', backlinkAssocName ] );
|
|
548
|
-
|
|
549
|
-
// rewrite $path
|
|
550
|
-
if(entityCsn.$path)
|
|
551
|
-
entityCsn.$path[1] = originalEntityName;
|
|
552
|
-
forEachMemberRecursively(entityCsn, (member) => {
|
|
553
|
-
if(member.$path)
|
|
554
|
-
member.$path[1] = originalEntityName;
|
|
555
|
-
});
|
|
588
|
+
setProp(parameterCsn.elements[parameterToTypeAssocName], '_target', entityCsn);
|
|
589
|
+
setProp(parameterCsn.elements[parameterToTypeAssocName], '$path',
|
|
590
|
+
[ 'definitions', parameterEntityName, 'elements', parameterToTypeAssocName ] );
|
|
556
591
|
}
|
|
592
|
+
// rewrite $path
|
|
593
|
+
setProp(parameterCsn, '$path', [ 'definitions', parameterEntityName ]);
|
|
557
594
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
Object.keys(entityCsn.$sources || {}).forEach(n => {
|
|
569
|
-
// preserve the original target for constraint calculation
|
|
570
|
-
setProp(entityCsn.$sources[n], '_originalTarget', entityCsn.$sources[n]._target);
|
|
571
|
-
entityCsn.$sources[n]._target = parameterCsn;
|
|
572
|
-
entityCsn.$sources[n].target = parameterCsn.name;
|
|
573
|
-
});
|
|
574
|
-
rewriteContainmentAnnotations(parameterCsn, entityCsn, parameterToOriginalAssocName);
|
|
595
|
+
// proxies are registered in model separately
|
|
596
|
+
if(!isProxy) {
|
|
597
|
+
if(csn.definitions[parameterCsn.name])
|
|
598
|
+
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
|
|
599
|
+
else {
|
|
600
|
+
csn.definitions[parameterCsn.name] = parameterCsn;
|
|
601
|
+
reqDefs.definitions[parameterCsn.name] = parameterCsn;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return parameterCsn;
|
|
575
605
|
}
|
|
576
606
|
|
|
577
|
-
|
|
578
607
|
function initElement(element, name, struct) {
|
|
579
608
|
setProp(element, 'name', name)
|
|
580
609
|
setProp(element, '_parent', struct);
|
|
@@ -600,7 +629,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
600
629
|
}
|
|
601
630
|
|
|
602
631
|
// Initialize a structured artifact
|
|
603
|
-
function
|
|
632
|
+
function initStructure(def) {
|
|
604
633
|
|
|
605
634
|
// Don't operate on any structured types other than type and entity
|
|
606
635
|
// such as events and aspects
|
|
@@ -611,24 +640,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
611
640
|
let validFrom = [], validKey = [];
|
|
612
641
|
|
|
613
642
|
// Iterate all struct elements
|
|
614
|
-
forEachMemberRecursively(def.items || def, (element, elementName, prop,
|
|
643
|
+
forEachMemberRecursively(def.items || def, (element, elementName, prop, _path = [], construct) => {
|
|
615
644
|
if(prop !== 'elements')
|
|
616
645
|
return;
|
|
617
646
|
|
|
618
647
|
initElement(element, elementName, construct);
|
|
619
648
|
|
|
620
|
-
|
|
621
|
-
if(element._parent && element._parent.$mySchemaName) {
|
|
622
|
-
if(!isODataSimpleIdentifier(elementName)) {
|
|
623
|
-
signalIllegalIdentifier(elementName, ['definitions', def.name].concat(path));
|
|
624
|
-
} else if (options.isV2() && /^(_|[0-9])/.test(elementName) && element._parent.kind === 'entity') {
|
|
625
|
-
// FIXME: Rewrite signalIllegalIdentifier function to be more flexible
|
|
626
|
-
error(null, ['definitions', def.name, 'elements', elementName], { prop: elementName[0] },
|
|
627
|
-
'Element names must not start with $(PROP) for OData V2');
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
// collect temporal information
|
|
649
|
+
// collect temporal information
|
|
632
650
|
if(element['@cds.valid.key']) {
|
|
633
651
|
validKey.push(element);
|
|
634
652
|
}
|
|
@@ -662,7 +680,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
662
680
|
applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
|
|
663
681
|
}, [], true, { elementsOnly: true });
|
|
664
682
|
|
|
665
|
-
if(!isDeprecatedEnabled(options, '
|
|
683
|
+
if(!isDeprecatedEnabled(options, '_v1KeysForTemporal')) {
|
|
666
684
|
// if artifact has a cds.valid.key mention it as @Core.AlternateKey
|
|
667
685
|
if(validKey.length) {
|
|
668
686
|
let altKeys = [{ Key: [] }];
|
|
@@ -708,18 +726,19 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
708
726
|
}
|
|
709
727
|
|
|
710
728
|
// Prepare the associations for the subsequent steps
|
|
711
|
-
function
|
|
712
|
-
if(!isStructuredArtifact(
|
|
729
|
+
function initConstraints(def) {
|
|
730
|
+
if(!isStructuredArtifact(def))
|
|
713
731
|
return;
|
|
714
732
|
|
|
715
|
-
forEachMemberRecursively(
|
|
716
|
-
|
|
733
|
+
forEachMemberRecursively(def.items || def, initConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
734
|
+
}
|
|
735
|
+
function initConstraintsOnAssoc(element) {
|
|
736
|
+
if (isAssociationOrComposition(element) && !element._constraints) {
|
|
717
737
|
// setup the constraints object
|
|
718
|
-
|
|
738
|
+
setProp(element, '_constraints', { constraints: Object.create(null), selfs: [], _origins: [], termCount: 0 });
|
|
719
739
|
// and crack the ON condition
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
}, [], true, { elementsOnly: true });
|
|
740
|
+
resolveOnConditionAndPrepareConstraints(csn, element, messageFunctions);
|
|
741
|
+
}
|
|
723
742
|
}
|
|
724
743
|
|
|
725
744
|
/*
|
|
@@ -766,7 +785,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
766
785
|
delete struct.$keys[element.name];
|
|
767
786
|
}
|
|
768
787
|
}
|
|
769
|
-
// deprecated
|
|
788
|
+
// deprecated._unmanagedUpInComponent:
|
|
770
789
|
// Only in containment:
|
|
771
790
|
// Ignore this (foreign key) elment if renderForeignKeys is false
|
|
772
791
|
if(options.odataContainment && element['@odata.containment.ignore']) {
|
|
@@ -791,38 +810,39 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
791
810
|
It may be that now a number of properties are not rendered and cannot act as constraints (see isConstraintCandidate())
|
|
792
811
|
in edmUtils
|
|
793
812
|
*/
|
|
794
|
-
function finalizeConstraints(
|
|
795
|
-
if(!isStructuredArtifact(
|
|
813
|
+
function finalizeConstraints(def) {
|
|
814
|
+
if(!isStructuredArtifact(def))
|
|
796
815
|
return;
|
|
797
816
|
|
|
798
|
-
forEachMemberRecursively(
|
|
799
|
-
|
|
800
|
-
|
|
817
|
+
forEachMemberRecursively(def.items || def, finalizeConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
818
|
+
}
|
|
819
|
+
function finalizeConstraintsOnAssoc(element) {
|
|
820
|
+
if (isAssociationOrComposition(element) && !element._ignore && element._constraints) {
|
|
821
|
+
finalizeReferentialConstraints(csn, element, options, info);
|
|
801
822
|
|
|
802
|
-
|
|
823
|
+
if(element._constraints._partnerCsn && element.cardinality && element.cardinality.max) {
|
|
803
824
|
// if this is a partnership and this assoc has a set target cardinality, assign it as source cardinality to the partner
|
|
804
|
-
|
|
825
|
+
if(element._constraints._partnerCsn.cardinality) {
|
|
805
826
|
// if the forward association has set a src cardinality and it deviates from the backlink target cardinality raise a warning
|
|
806
827
|
// in V2 only, in V4 the source cardinality is rendered implicitly at the Type property
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
828
|
+
if(element._constraints._partnerCsn.cardinality.src) {
|
|
829
|
+
let srcMult = (element._constraints._partnerCsn.cardinality.src == 1) ? '0..1' : '*';
|
|
830
|
+
let newMult = (element.cardinality.max > 1) ? '*' : '0..1';
|
|
831
|
+
if(options.isV2() && srcMult !== newMult) {
|
|
811
832
|
// Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
|
|
812
|
-
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
else {
|
|
816
|
-
// .. but only if the original assoc hasn't set src yet
|
|
817
|
-
element._constraints._partnerCsn.cardinality.src = element.cardinality.max;
|
|
833
|
+
warning(null, null, `Source cardinality "${element._constraints._partnerCsn.cardinality.src}" of "${element._constraints._partnerCsn._parent.name}/${element._constraints._partnerCsn.name}" conflicts with target cardinality "${element.cardinality.max}" of association "${element._parent.name}/${element.name}"`);
|
|
818
834
|
}
|
|
819
835
|
}
|
|
820
836
|
else {
|
|
821
|
-
|
|
837
|
+
// .. but only if the original assoc hasn't set src yet
|
|
838
|
+
element._constraints._partnerCsn.cardinality.src = element.cardinality.max;
|
|
822
839
|
}
|
|
823
840
|
}
|
|
841
|
+
else {
|
|
842
|
+
element._constraints._partnerCsn.cardinality = { src: element.cardinality.max };
|
|
843
|
+
}
|
|
824
844
|
}
|
|
825
|
-
}
|
|
845
|
+
}
|
|
826
846
|
}
|
|
827
847
|
|
|
828
848
|
/*
|
|
@@ -830,7 +850,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
830
850
|
sub artifacts exposed by the initial type exposure
|
|
831
851
|
*/
|
|
832
852
|
function convertExposedTypesOfOtherServicesIntoCrossReferences() {
|
|
833
|
-
if(options.
|
|
853
|
+
if(options.odataXServiceRefs && options.isV4()) {
|
|
834
854
|
serviceRootNames.forEach(srn => {
|
|
835
855
|
schemaNames.forEach(fqSchemaName => {
|
|
836
856
|
if(fqSchemaName.startsWith(srn + '.')) {
|
|
@@ -838,8 +858,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
838
858
|
if(serviceRootNames.includes(targetSchemaName)) {
|
|
839
859
|
// remove all definitions starting with < fqSchemaName >. and add a schema reference
|
|
840
860
|
Object.keys(csn.definitions).forEach(dn => {
|
|
841
|
-
if(dn.startsWith(fqSchemaName)) // this includes the fqSchemaName context
|
|
861
|
+
if(dn.startsWith(fqSchemaName)) {// this includes the fqSchemaName context
|
|
842
862
|
delete csn.definitions[dn];
|
|
863
|
+
delete reqDefs.definitions[dn];
|
|
864
|
+
}
|
|
843
865
|
});
|
|
844
866
|
if(!schemas[fqSchemaName])
|
|
845
867
|
schemaNames.push(fqSchemaName);
|
|
@@ -870,10 +892,9 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
870
892
|
|
|
871
893
|
If option odataExtReferences is used, 'root' proxies are still created.
|
|
872
894
|
|
|
873
|
-
If
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
proxies.
|
|
895
|
+
If the association leading to the proxy candidate refers to associations either directly
|
|
896
|
+
or indirectly (via structured elements), these dependent entity types are (recursively) exposed
|
|
897
|
+
(or referenced) as well to keep the navigation graph in tact.
|
|
877
898
|
*/
|
|
878
899
|
function exposeTargetsAsProxiesOrSchemaRefs(struct) {
|
|
879
900
|
if(struct.kind === 'context' || struct.kind === 'service' || struct.$proxy)
|
|
@@ -886,7 +907,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
886
907
|
// if this artifact is a service member check its associations
|
|
887
908
|
if(globalSchemaPrefix) {
|
|
888
909
|
forEachGeneric(struct.items || struct, 'elements', element => {
|
|
889
|
-
if(!isAssociationOrComposition(element) || element
|
|
910
|
+
if(!isAssociationOrComposition(element) || element['@odata.navigable'] === false)
|
|
890
911
|
return;
|
|
891
912
|
/*
|
|
892
913
|
* Consider everything @cds.autoexpose: falsy to be a proxy candidate for now
|
|
@@ -923,16 +944,18 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
923
944
|
const targetSchemaName = element._target.$mySchemaName;
|
|
924
945
|
if(isProxyRequired(element)) {
|
|
925
946
|
if(options.isV4() &&
|
|
926
|
-
|
|
927
|
-
// must be a managed association with keys
|
|
928
|
-
|
|
947
|
+
(options.odataProxies || options.odataXServiceRefs) &&
|
|
948
|
+
// must be a managed association with keys OR an unambiguous backlink
|
|
949
|
+
element.keys ||
|
|
950
|
+
(element.on && element._constraints.selfs.length === 1 && element._constraints.termCount === 1)
|
|
951
|
+
) {
|
|
929
952
|
// reuse proxy if available
|
|
930
953
|
let proxy = getProxyForTargetOf(element);
|
|
931
954
|
if(!proxy) {
|
|
932
|
-
if(targetSchemaName && options.
|
|
933
|
-
proxy = createSchemaRefFor(
|
|
955
|
+
if(targetSchemaName && options.odataXServiceRefs) {
|
|
956
|
+
proxy = createSchemaRefFor(targetSchemaName);
|
|
934
957
|
}
|
|
935
|
-
else if(options.
|
|
958
|
+
else if(options.odataProxies) {
|
|
936
959
|
proxy = createProxyFor(element, targetSchemaName);
|
|
937
960
|
}
|
|
938
961
|
proxy = registerProxy(proxy, element);
|
|
@@ -941,14 +964,16 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
941
964
|
// if a proxy was either already created or could be created and
|
|
942
965
|
// if it's a 'real' proxy, link the _target to it and remove constraints
|
|
943
966
|
// otherwise proxy is a schema reference, then do nothing
|
|
967
|
+
setProp(element, '$noPartner', true);
|
|
944
968
|
element._constraints.constraints = Object.create(null);
|
|
945
969
|
if(proxy.kind === 'entity') {
|
|
970
|
+
if(!proxy.$isParamEntity)
|
|
971
|
+
populateProxyElements(element, proxy, getForeignKeyDefinitions(element));
|
|
946
972
|
element._target = proxy;
|
|
947
|
-
populateProxyElements(element, proxy, getForeignKeyDefinitions(element, ['definitions', struct.name, 'elements', element.name]));
|
|
948
973
|
}
|
|
949
974
|
else {
|
|
950
|
-
//
|
|
951
|
-
setProp(element
|
|
975
|
+
// No navigation property bindings on external references
|
|
976
|
+
setProp(element, '$externalRef', true);
|
|
952
977
|
}
|
|
953
978
|
}
|
|
954
979
|
else {
|
|
@@ -968,11 +993,11 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
968
993
|
}
|
|
969
994
|
|
|
970
995
|
function noNavPropMsg(elt) {
|
|
971
|
-
warning(
|
|
972
|
-
{ target: elt._target.name, service: globalSchemaPrefix }
|
|
996
|
+
warning('odata-navigation', ['definitions', struct.name, 'elements', elt.name],
|
|
997
|
+
{ target: elt._target.name, service: globalSchemaPrefix });
|
|
973
998
|
}
|
|
974
999
|
|
|
975
|
-
function createSchemaRefFor(
|
|
1000
|
+
function createSchemaRefFor(targetSchemaName) {
|
|
976
1001
|
let ref = csn.definitions[globalSchemaPrefix + '.' + targetSchemaName];
|
|
977
1002
|
if(!ref) {
|
|
978
1003
|
ref = createSchemaRef(targetSchemaName);
|
|
@@ -987,11 +1012,23 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
987
1012
|
// if it is required in multiple services. The service schema name is prepended upon registration
|
|
988
1013
|
const proxySchemaName = targetSchemaName || getSchemaPrefix(assoc._target.name);
|
|
989
1014
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1015
|
+
// if the target is a parameter entity, it's easy just create the parameter stub
|
|
1016
|
+
const isParamProxy = isParameterizedEntity(assoc._target);
|
|
1017
|
+
|
|
1018
|
+
// 1) construct the proxy definition
|
|
1019
|
+
// proxyDefinitionName: strip the serviceName and replace '.' with '_'
|
|
1020
|
+
let defName =
|
|
1021
|
+
`${assoc._target.name.replace(proxySchemaName + '.', '').replace(/\./g, '_')}`;
|
|
1022
|
+
|
|
993
1023
|
// fullName: Prepend serviceName and if in same service add '_proxy'
|
|
994
|
-
const proxy =
|
|
1024
|
+
const proxy = isParamProxy
|
|
1025
|
+
? createParameterEntity(assoc._target, proxySchemaName + '.' + defName, true)
|
|
1026
|
+
: { name: proxySchemaName + '.' + defName, kind: 'entity', elements: Object.create(null) };
|
|
1027
|
+
|
|
1028
|
+
// Final proxyShortName for all further processing
|
|
1029
|
+
const proxyShortName = defName + (isParamProxy ? 'Parameters' : '');
|
|
1030
|
+
|
|
1031
|
+
setProp(proxy, '$proxy', true);
|
|
995
1032
|
setProp(proxy, '$mySchemaName', proxySchemaName);
|
|
996
1033
|
setProp(proxy, '$proxyShortName', proxyShortName);
|
|
997
1034
|
setProp(proxy, '$keys', Object.create(null));
|
|
@@ -1004,53 +1041,66 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1004
1041
|
});
|
|
1005
1042
|
|
|
1006
1043
|
// 2) create the elements and $keys
|
|
1007
|
-
|
|
1044
|
+
if(isParamProxy) {
|
|
1045
|
+
// Reset param proxy elements to expose element tree
|
|
1046
|
+
const elements = proxy.elements;
|
|
1047
|
+
proxy.elements = Object.create(null);
|
|
1048
|
+
populateProxyElements(assoc, proxy, elements);
|
|
1049
|
+
}
|
|
1050
|
+
else
|
|
1051
|
+
populateProxyElements(assoc, proxy, assoc._target.$keys);
|
|
1008
1052
|
return proxy;
|
|
1009
1053
|
|
|
1010
1054
|
}
|
|
1011
1055
|
|
|
1012
|
-
//
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}).filter(fk=>fk) : [];
|
|
1056
|
+
// Return top level foreign key element definitions. The full top level
|
|
1057
|
+
// element is exposed instead of merging partial trees into the exposed type
|
|
1058
|
+
// def structure.
|
|
1059
|
+
function getForeignKeyDefinitions(e) {
|
|
1060
|
+
return e.keys ? e.keys.map(fk => e._target.elements[fk.ref[0]]) : [];
|
|
1018
1061
|
}
|
|
1019
|
-
|
|
1062
|
+
|
|
1063
|
+
// copy over the primary keys of the target and trigger the type exposure
|
|
1064
|
+
// if the element already exists we assume it was fully exposed
|
|
1020
1065
|
function populateProxyElements(assoc, proxy, elements) {
|
|
1021
1066
|
forAll(elements, e => {
|
|
1022
|
-
if (isEdmPropertyRendered(e, options)
|
|
1023
|
-
let newElt =
|
|
1024
|
-
if(
|
|
1025
|
-
if(
|
|
1026
|
-
if(
|
|
1027
|
-
|
|
1028
|
-
|
|
1067
|
+
if (isEdmPropertyRendered(e, options)) {
|
|
1068
|
+
let newElt = proxy.elements[e.name];
|
|
1069
|
+
if(!newElt) {
|
|
1070
|
+
if(csnUtils.isAssocOrComposition(e.type)) {
|
|
1071
|
+
if(!e.on && e.keys) {
|
|
1072
|
+
if(options.odataNoTransitiveProxies)
|
|
1073
|
+
newElt = convertManagedAssocIntoStruct(e);
|
|
1074
|
+
else
|
|
1029
1075
|
newElt = createProxyOrSchemaRefForManagedAssoc(e);
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
info(null, ['definitions', struct.name, 'elements', assoc.name],
|
|
1033
1079
|
{ name: proxy.nname, target: assoc._target.name },
|
|
1034
1080
|
'Unmanaged associations are not supported as primary keys for proxy entity type $(NAME) of unexposed association target $(TARGET)');
|
|
1081
|
+
}
|
|
1035
1082
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1083
|
+
else {
|
|
1084
|
+
newElt = Object.create(null);
|
|
1085
|
+
Object.keys(e).forEach(prop => newElt[prop] = e[prop])
|
|
1086
|
+
}
|
|
1087
|
+
if(newElt) {
|
|
1088
|
+
initElement(newElt, e.name, proxy);
|
|
1089
|
+
proxy.elements[newElt.name] = newElt;
|
|
1090
|
+
|
|
1091
|
+
if(csnUtils.isStructured(newElt)) {
|
|
1043
1092
|
// argument proxySchemaName forces an anonymous type definition for newElt into the
|
|
1044
|
-
// proxy schema. If omitted, this exposure defaults to 'root', in case API flavor
|
|
1045
|
-
// changes...
|
|
1046
|
-
|
|
1047
|
-
|
|
1093
|
+
// proxy schema. If omitted, this exposure defaults to 'root', in case API flavor
|
|
1094
|
+
// of the day changes...
|
|
1095
|
+
exposeStructTypeForProxyOf(proxy, newElt, proxy.$proxyShortName + '_' + newElt.name,
|
|
1096
|
+
proxy.$mySchemaName, newElt.key, !!(newElt.key && newElt.elements));
|
|
1097
|
+
}
|
|
1098
|
+
if(newElt.key)
|
|
1099
|
+
proxy.$keys[newElt.name] = newElt;
|
|
1048
1100
|
}
|
|
1049
|
-
proxy.elements[newElt.name] = newElt;
|
|
1050
|
-
if(newElt.key)
|
|
1051
|
-
proxy.$keys[newElt.name] = newElt;
|
|
1052
1101
|
}
|
|
1053
1102
|
}
|
|
1103
|
+
|
|
1054
1104
|
});
|
|
1055
1105
|
// 3) sort the exposed types so that they appear lexicographically ordered in the EDM
|
|
1056
1106
|
proxy.$exposedTypes = Object.keys(proxy.$exposedTypes).sort().reduce((dict, tn) => {
|
|
@@ -1062,78 +1112,90 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1062
1112
|
// anonymous or has a definition outside of 'service'), create an equivalent type in 'service', either
|
|
1063
1113
|
// using the type's name or (if anonymous) 'artificialName', and make 'node' use that type instead.
|
|
1064
1114
|
// Complain if there is an error.
|
|
1065
|
-
|
|
1066
|
-
|
|
1115
|
+
// isKey: Indicates top level element is key or not
|
|
1116
|
+
// forceToNotNull: if top level element is key, recursively set all anonymously exposed elements
|
|
1117
|
+
// to notNull until the first named type is exposed.
|
|
1118
|
+
function exposeStructTypeForProxyOf(proxy, node, artificialName,
|
|
1119
|
+
typeSchemaName=fallBackSchemaName,
|
|
1120
|
+
isKey, forceToNotNull) {
|
|
1121
|
+
|
|
1122
|
+
if(node.type && isBuiltinType(node.type))
|
|
1123
|
+
return;
|
|
1124
|
+
|
|
1067
1125
|
// Always expose types referred to by a proxy, never reuse an eventually existing type
|
|
1068
1126
|
// as the nested elements must all be not nullable
|
|
1069
|
-
|
|
1070
|
-
|
|
1127
|
+
// elements have precedence over type
|
|
1128
|
+
const typeDef = !node.elements && node.type ? csn.definitions[node.type] : node;
|
|
1071
1129
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1130
|
+
if (typeDef) {
|
|
1131
|
+
let typeClone;
|
|
1074
1132
|
// the type clone must be produced for each service as this type may
|
|
1075
1133
|
// produce references and/or proxies into multiple services
|
|
1076
1134
|
// (but only once per service, therefore cache it).
|
|
1077
|
-
|
|
1135
|
+
if(typeDef.$proxyTypes && typeDef.$proxyTypes[globalSchemaPrefix]) {
|
|
1078
1136
|
// if type has been exposed in a schema use this type
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1137
|
+
typeClone = typeDef.$proxyTypes[globalSchemaPrefix];
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1082
1140
|
// Set the correct name
|
|
1083
|
-
|
|
1084
|
-
|
|
1141
|
+
let typeId = artificialName; // the artificialName has no namespace, it's the element
|
|
1142
|
+
if(node.type) {
|
|
1085
1143
|
// same as for proxies, use schema or namespace, 'root' is last resort
|
|
1086
|
-
|
|
1087
|
-
|
|
1144
|
+
typeSchemaName = typeDef.$mySchemaName || getSchemaPrefix(node.type);
|
|
1145
|
+
typeId = node.type.replace(typeSchemaName + '.', '').replace(/\./g, '_');
|
|
1088
1146
|
// strip the service root of that type (if any)
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1147
|
+
const myServiceRootName = whatsMyServiceRootName(typeSchemaName);
|
|
1148
|
+
if(myServiceRootName)
|
|
1149
|
+
typeSchemaName = typeSchemaName.replace(myServiceRootName + '.', '');
|
|
1150
|
+
}
|
|
1093
1151
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1152
|
+
if(isStructuredArtifact(typeDef)) {
|
|
1153
|
+
// pull forceNotNull to false for named types and non-key nodes
|
|
1154
|
+
// only toplevel nodes (elements) can be key
|
|
1155
|
+
forceToNotNull = !!(forceToNotNull && isKey && node.elements && !node.type);
|
|
1156
|
+
|
|
1157
|
+
typeClone = cloneStructTypeForProxy(typeSchemaName, `${typeSchemaName}.${typeId}`, typeDef);
|
|
1158
|
+
if(typeClone) {
|
|
1097
1159
|
// Recurse into elements of 'type' (if any)
|
|
1098
|
-
|
|
1160
|
+
typeClone.elements && Object.entries(typeClone.elements).forEach(([elemName, elem]) => {
|
|
1099
1161
|
// if this is a foreign key elment, we must check whether or not the association
|
|
1100
1162
|
// has been exposed as proxy. If it has not been exposed, no further structured
|
|
1101
1163
|
// types must be exposed as 'Proxy_' types.
|
|
1102
1164
|
|
|
1103
1165
|
// TODO: expose types of assoc.keys and don't rely on exposed foreign keys
|
|
1104
|
-
|
|
1166
|
+
if(!elem['@odata.foreignKey4'] ||
|
|
1105
1167
|
(elem['@odata.foreignKey4'] && !typeClone.elements[elem['@odata.foreignKey4']].$exposed))
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
typeDef.$proxyTypes
|
|
1111
|
-
|
|
1168
|
+
exposeStructTypeForProxyOf(proxy, elem, `${typeId}_${elemName}`,
|
|
1169
|
+
typeSchemaName, isKey, forceToNotNull);
|
|
1170
|
+
});
|
|
1171
|
+
if(!typeDef.$proxyTypes)
|
|
1172
|
+
typeDef.$proxyTypes = Object.create(null);
|
|
1173
|
+
typeDef.$proxyTypes[globalSchemaPrefix] = typeClone;
|
|
1112
1174
|
}
|
|
1113
|
-
|
|
1175
|
+
}
|
|
1176
|
+
else {
|
|
1114
1177
|
// FUTURE: expose scalar type definition as well
|
|
1115
|
-
}
|
|
1116
1178
|
}
|
|
1117
|
-
|
|
1179
|
+
}
|
|
1180
|
+
if(typeClone) {
|
|
1118
1181
|
// register the type clone at the proxy
|
|
1119
1182
|
// Reminder: Each proxy receives a full set of type clones, even if the types are shared
|
|
1120
1183
|
// (no scattered type clone caching). registerProxy() checks if a clone needs to be added to
|
|
1121
1184
|
// csn.definitions.
|
|
1122
|
-
|
|
1185
|
+
proxy.$exposedTypes[typeClone.name] = typeClone;
|
|
1123
1186
|
|
|
1124
1187
|
// set the node's new type name
|
|
1125
|
-
|
|
1188
|
+
node.type = typeClone.name;
|
|
1126
1189
|
// the key path generator must use the type clone directly, because it can't resolve
|
|
1127
1190
|
// the type clone in the CSN (its name is the final name and not the definition name).
|
|
1128
|
-
|
|
1191
|
+
setProp(node, '_type', typeClone);
|
|
1129
1192
|
// Hack alert:
|
|
1130
1193
|
// beta feature 'subElemRedirections' (now the default in v2) adds elements to the node by
|
|
1131
1194
|
// default, without we must do it to get the primary key tuple calculation correct.
|
|
1132
1195
|
// Remember: node.type is the service local type name (not prepended by the service name),
|
|
1133
1196
|
// so it can't be resolved in definitions later on
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
}
|
|
1197
|
+
if(typeClone.elements)
|
|
1198
|
+
node.elements = typeClone.elements;
|
|
1137
1199
|
}
|
|
1138
1200
|
}
|
|
1139
1201
|
|
|
@@ -1151,12 +1213,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1151
1213
|
if(!elem.target) {
|
|
1152
1214
|
type.elements[elemName] = Object.create(null);
|
|
1153
1215
|
Object.keys(elem).forEach(prop => type.elements[elemName][prop] = elem[prop])
|
|
1154
|
-
type.elements[elemName].notNull = true;
|
|
1155
1216
|
}
|
|
1156
1217
|
else if(elem.keys && !elem.on) {
|
|
1157
1218
|
// a primary key can never be an unmanaged association
|
|
1158
1219
|
type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
|
|
1159
1220
|
}
|
|
1221
|
+
if(forceToNotNull)
|
|
1222
|
+
type.elements[elemName].notNull = true;
|
|
1160
1223
|
setProp(type.elements[elemName], 'name', elem.name);
|
|
1161
1224
|
});
|
|
1162
1225
|
return type;
|
|
@@ -1166,7 +1229,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1166
1229
|
// Convert a managed association into a structured type and
|
|
1167
1230
|
// eliminate nested foreign key associations
|
|
1168
1231
|
function convertManagedAssocIntoStruct(e) {
|
|
1169
|
-
let newElt =
|
|
1232
|
+
let newElt = cloneCsnNonDict(e, options);
|
|
1170
1233
|
newElt.elements = Object.create(null);
|
|
1171
1234
|
// remove all unwanted garbage
|
|
1172
1235
|
delete newElt.keys;
|
|
@@ -1177,14 +1240,15 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1177
1240
|
let keys = (!e._target.$isParamEntity && e.keys) ||
|
|
1178
1241
|
Object.keys(e._target.$keys).map(k => { return { ref: [k] } });
|
|
1179
1242
|
keys.forEach(k => {
|
|
1180
|
-
let art = e._target || getCsnDef(e.target);
|
|
1243
|
+
let art = e._target || csnUtils.getCsnDef(e.target);
|
|
1181
1244
|
for(let ps of k.ref) {
|
|
1182
1245
|
art = art.elements[ps];
|
|
1183
1246
|
}
|
|
1184
1247
|
// art is in the target side, clone it and remove key property
|
|
1185
|
-
let cloneArt =
|
|
1248
|
+
let cloneArt = cloneCsnNonDict(art, options);
|
|
1186
1249
|
setProp(cloneArt, 'name', art.name);
|
|
1187
|
-
|
|
1250
|
+
if(e.key)
|
|
1251
|
+
cloneArt.notNull = true;
|
|
1188
1252
|
delete cloneArt.key;
|
|
1189
1253
|
newElt.elements[art.name] = cloneArt;
|
|
1190
1254
|
});
|
|
@@ -1200,18 +1264,19 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1200
1264
|
function createProxyOrSchemaRefForManagedAssoc(e) {
|
|
1201
1265
|
|
|
1202
1266
|
let proxy = e._target;
|
|
1203
|
-
let newElt =
|
|
1267
|
+
let newElt = cloneCsnNonDict(e, options);
|
|
1204
1268
|
|
|
1205
1269
|
if(isProxyRequired(e)) {
|
|
1206
1270
|
proxy = getProxyForTargetOf(e);
|
|
1207
1271
|
if(!proxy) {
|
|
1208
1272
|
// option odataXServiceRefs has precedence over odataProxies
|
|
1209
|
-
if(e._target.$mySchemaName && options.
|
|
1210
|
-
proxy = createSchemaRefFor(e
|
|
1273
|
+
if(e._target.$mySchemaName && options.odataXServiceRefs) {
|
|
1274
|
+
proxy = createSchemaRefFor(e._target.$mySchemaName);
|
|
1211
1275
|
}
|
|
1212
|
-
else if(options.
|
|
1276
|
+
else if(options.odataProxies) {
|
|
1213
1277
|
proxy = createProxyFor(e, e._target.$mySchemaName);
|
|
1214
|
-
|
|
1278
|
+
if(!e._target.$isParamEntity)
|
|
1279
|
+
populateProxyElements(e, proxy, getForeignKeyDefinitions(e));
|
|
1215
1280
|
}
|
|
1216
1281
|
proxy = registerProxy(proxy, e);
|
|
1217
1282
|
}
|
|
@@ -1227,7 +1292,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1227
1292
|
setProp(newElt, '$exposed', true);
|
|
1228
1293
|
// _target must be set with (original) in case
|
|
1229
1294
|
// a schema ref has been created
|
|
1295
|
+
setProp(newElt, '$noPartner', true);
|
|
1230
1296
|
setProp(newElt, '_target', e._target);
|
|
1297
|
+
initConstraintsOnAssoc(e);
|
|
1298
|
+
finalizeConstraintsOnAssoc(e);
|
|
1231
1299
|
setProp(newElt, '_constraints', e._constraints);
|
|
1232
1300
|
setProp(newElt, '_selfReferences', []);
|
|
1233
1301
|
if(proxy.kind === 'entity') {
|
|
@@ -1322,11 +1390,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1322
1390
|
const alreadyRegistered = csn.definitions[fqProxyName];
|
|
1323
1391
|
if(!alreadyRegistered) {
|
|
1324
1392
|
csn.definitions[fqProxyName] = proxy;
|
|
1393
|
+
reqDefs.definitions[fqProxyName] = proxy;
|
|
1325
1394
|
setProp(proxy, '$path', ['definitions', fqProxyName]);
|
|
1326
1395
|
Object.entries(proxy.$exposedTypes).forEach(([tn, v]) => {
|
|
1327
1396
|
const fqtn = proxy.$globalSchemaPrefix + '.' + tn;
|
|
1328
1397
|
if(csn.definitions[fqtn] === undefined) {
|
|
1329
1398
|
csn.definitions[fqtn] = v;
|
|
1399
|
+
reqDefs.definitions[fqtn] = v;
|
|
1330
1400
|
setProp(v, '$path', ['definitions', fqtn]);
|
|
1331
1401
|
}
|
|
1332
1402
|
});
|
|
@@ -1372,28 +1442,25 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1372
1442
|
and do not render associations (this will include the foreign keys of
|
|
1373
1443
|
the _isToContainer association).
|
|
1374
1444
|
*/
|
|
1375
|
-
function
|
|
1376
|
-
if(
|
|
1377
|
-
setProp(
|
|
1445
|
+
function initEdmKeyRefPaths(def) {
|
|
1446
|
+
if(def.$keys) {
|
|
1447
|
+
setProp(def, '$edmKeyPaths', []);
|
|
1378
1448
|
// for all key elements that shouldn't be ignored produce the paths
|
|
1379
|
-
foreach(
|
|
1449
|
+
foreach(def.$keys, k => !(k._isToContainer && k._selfReferences.length), (k, kn) => {
|
|
1380
1450
|
if(isEdmPropertyRendered(k, options) &&
|
|
1381
1451
|
!(options.isV2() && k['@Core.MediaType'])) {
|
|
1382
1452
|
if(options.isV4() && options.isStructFormat) {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if(options.renderForeignKeys && !k.target)
|
|
1386
|
-
|
|
1387
|
-
// else produce paths (isEdmPropertyRendered() has filtered @odata.foreignKey4 already)
|
|
1388
|
-
else if(!options.renderForeignKeys)
|
|
1389
|
-
struct.$edmKeyPaths.push(...produceKeyRefPaths(k, kn));
|
|
1453
|
+
// This is structured OData ONLY
|
|
1454
|
+
// if the foreign keys are explicitly requested, ignore associations and use the flat foreign keys instead
|
|
1455
|
+
if(!options.renderForeignKeys || (options.renderForeignKeys && !k.target))
|
|
1456
|
+
def.$edmKeyPaths.push(...produceKeyRefPaths(k, kn));
|
|
1390
1457
|
}
|
|
1391
1458
|
// In v2/v4 flat, associations are never rendered
|
|
1392
1459
|
else if(!k.target) {
|
|
1393
|
-
|
|
1460
|
+
def.$edmKeyPaths.push([kn]);
|
|
1394
1461
|
}
|
|
1395
1462
|
// check toplevel key for spec violations
|
|
1396
|
-
checkKeySpecViolations(k, ['definitions',
|
|
1463
|
+
checkKeySpecViolations(k, ['definitions', def.name, 'elements', k.name]);
|
|
1397
1464
|
}
|
|
1398
1465
|
});
|
|
1399
1466
|
}
|
|
@@ -1410,6 +1477,8 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1410
1477
|
*/
|
|
1411
1478
|
function produceKeyRefPaths(eltCsn, prefix) {
|
|
1412
1479
|
const keyPaths = [];
|
|
1480
|
+
// we want to point to the element in the entity which is the first path step
|
|
1481
|
+
const location = def.$path.concat(['elements']).concat(prefix.split('/')[0]);
|
|
1413
1482
|
if(!isEdmPropertyRendered(eltCsn, options)) {
|
|
1414
1483
|
// let annos = Object.keys(eltCsn).filter(a=>a[0]==='@').join(', ');
|
|
1415
1484
|
// warning(null, ['definitions', struct.name, 'elements', eltCsn.name ],
|
|
@@ -1418,7 +1487,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1418
1487
|
}
|
|
1419
1488
|
// OData requires all elements along the path to be nullable: false (that is either key or notNull)
|
|
1420
1489
|
|
|
1421
|
-
const finalType = getFinalTypeDef(eltCsn.items && eltCsn.items.type || eltCsn.type);
|
|
1490
|
+
const finalType = csnUtils.getFinalTypeDef(eltCsn.items && eltCsn.items.type || eltCsn.type);
|
|
1422
1491
|
const elements = eltCsn.elements || eltCsn.items && eltCsn.items.elements ||
|
|
1423
1492
|
(finalType && (finalType.elements || finalType.items && finalType.items.elements));
|
|
1424
1493
|
if(elements) {
|
|
@@ -1428,8 +1497,6 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1428
1497
|
keyPaths.push(...newRefs);
|
|
1429
1498
|
// check path step key for spec violations
|
|
1430
1499
|
const pathSegment = `${prefix}/${eltName}`;
|
|
1431
|
-
// we want to point to the element in the entity which is the first path step
|
|
1432
|
-
const location = struct.$path.concat(['elements']).concat(pathSegment.split('/')[0]);
|
|
1433
1500
|
checkKeySpecViolations(elt, location, pathSegment);
|
|
1434
1501
|
}
|
|
1435
1502
|
});
|
|
@@ -1444,12 +1511,15 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1444
1511
|
// use the primary keys of the target
|
|
1445
1512
|
let keys = (!eltCsn._target.$isParamEntity && eltCsn.keys) ||
|
|
1446
1513
|
Object.keys(eltCsn._target.$keys).map(k => { return { ref: [k] } });
|
|
1514
|
+
let pathSegment = prefix
|
|
1447
1515
|
keys.forEach(k => {
|
|
1448
|
-
let art = eltCsn._target || getCsnDef(eltCsn.target);
|
|
1516
|
+
let art = eltCsn._target || csnUtils.getCsnDef(eltCsn.target);
|
|
1449
1517
|
for(let ps of k.ref) {
|
|
1450
1518
|
art = art.elements[ps];
|
|
1519
|
+
pathSegment += '/' + art.name
|
|
1520
|
+
checkKeySpecViolations(art, location, pathSegment);
|
|
1451
1521
|
if(art.type && !isBuiltinType(art.type)) {
|
|
1452
|
-
art = art._type || getCsnDef(art.type);
|
|
1522
|
+
art = art._type || csnUtils.getCsnDef(art.type);
|
|
1453
1523
|
}
|
|
1454
1524
|
}
|
|
1455
1525
|
keyPaths.push(...produceKeyRefPaths(art, prefix + options.pathDelimiter + k.ref.join(options.pathDelimiter)));
|
|
@@ -1465,13 +1535,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1465
1535
|
// Nullability
|
|
1466
1536
|
if((!elt.key && (elt.notNull === undefined || elt.notNull === false)) ||
|
|
1467
1537
|
elt.key && (elt.notNull !== undefined && elt.notNull === false)) {
|
|
1468
|
-
|
|
1538
|
+
message('odata-spec-violation-key-null', location,
|
|
1469
1539
|
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1470
1540
|
}
|
|
1471
1541
|
// many
|
|
1472
|
-
let type = elt.items || elt.type && !isBuiltinType(elt.type) && getFinalTypeDef(elt.type).items;
|
|
1542
|
+
let type = elt.items || elt.type && !isBuiltinType(elt.type) && csnUtils.getFinalTypeDef(elt.type).items;
|
|
1473
1543
|
if(type) {
|
|
1474
|
-
|
|
1544
|
+
message('odata-spec-violation-key-array', location,
|
|
1475
1545
|
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1476
1546
|
}
|
|
1477
1547
|
// type
|
|
@@ -1483,10 +1553,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1483
1553
|
// V2 allows any Edm.PrimitiveType (even Double and Binary), V4 is more specific:
|
|
1484
1554
|
if(options.isV4() && type && !isAssociationOrComposition(type) && isBuiltinType(type.type)) {
|
|
1485
1555
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1486
|
-
const legalEdmTypes =
|
|
1487
|
-
'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Duration',
|
|
1488
|
-
'Edm.Guid', 'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte', 'Edm.String', 'Edm.TimeOfDay'
|
|
1489
|
-
if(!
|
|
1556
|
+
const legalEdmTypes = {
|
|
1557
|
+
'Edm.Boolean':1, 'Edm.Byte':1, 'Edm.Date':1, 'Edm.DateTimeOffset':1, 'Edm.Decimal':1, 'Edm.Duration':1,
|
|
1558
|
+
'Edm.Guid':1, 'Edm.Int16':1, 'Edm.Int32':1, 'Edm.Int64':1, 'Edm.SByte':1, 'Edm.String':1, 'Edm.TimeOfDay':1 };
|
|
1559
|
+
if(!(edmType in legalEdmTypes)) {
|
|
1490
1560
|
warning('odata-spec-violation-key-type', location,
|
|
1491
1561
|
{name: pathSegment, type: type.type, id: edmType, '#': pathSegment ? 'std' : 'scalar'});
|
|
1492
1562
|
}
|
|
@@ -1522,10 +1592,10 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1522
1592
|
Path="items/subitems/subitems/up_" Target="Header/items/subitems"/>
|
|
1523
1593
|
Path="items/subitems/subitems/toG" Target="G"/>
|
|
1524
1594
|
*/
|
|
1525
|
-
function
|
|
1526
|
-
if(
|
|
1527
|
-
forEachGeneric(
|
|
1528
|
-
produceTargetPath([edmUtils.getBaseName(
|
|
1595
|
+
function initEdmNavPropBindingTargets(def) {
|
|
1596
|
+
if(def.$hasEntitySet) {
|
|
1597
|
+
forEachGeneric(def.items || def, 'elements', (element) => {
|
|
1598
|
+
produceTargetPath([edmUtils.getBaseName(def.name)], element, def);
|
|
1529
1599
|
});
|
|
1530
1600
|
}
|
|
1531
1601
|
|
|
@@ -1537,7 +1607,14 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1537
1607
|
if(isAssociationOrComposition(elt) && !elt.$touched) {
|
|
1538
1608
|
if(!elt._target.$edmTgtPaths)
|
|
1539
1609
|
setProp(elt._target, '$edmTgtPaths', []);
|
|
1540
|
-
|
|
1610
|
+
// drill into target only if
|
|
1611
|
+
// 1) target has no entity set and this assoc is not going to the container
|
|
1612
|
+
// 2) current definition and target are the same (cycle)
|
|
1613
|
+
// 3) it's no external reference
|
|
1614
|
+
if(!elt.$externalRef &&
|
|
1615
|
+
!elt._target.$hasEntitySet &&
|
|
1616
|
+
!elt._isToContainer &&
|
|
1617
|
+
curDef !== elt._target) {
|
|
1541
1618
|
// follow elements in the target but avoid cycles
|
|
1542
1619
|
setProp(elt, '$touched', true);
|
|
1543
1620
|
elt._target.$edmTgtPaths.push(newPrefix);
|
|
@@ -1556,13 +1633,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1556
1633
|
}
|
|
1557
1634
|
}
|
|
1558
1635
|
|
|
1559
|
-
function
|
|
1560
|
-
if(options.isV4() &&
|
|
1636
|
+
function initEdmNavPropBindingPaths(def) {
|
|
1637
|
+
if(options.isV4() &&def.$hasEntitySet) {
|
|
1561
1638
|
let npbs = [];
|
|
1562
|
-
forEachGeneric(
|
|
1563
|
-
npbs = npbs.concat(produceNavigationPath(element,
|
|
1639
|
+
forEachGeneric(def.items || def, 'elements', (element) => {
|
|
1640
|
+
npbs = npbs.concat(produceNavigationPath(element, def));
|
|
1564
1641
|
});
|
|
1565
|
-
setProp(
|
|
1642
|
+
setProp(def, '$edmNPBs', npbs);
|
|
1566
1643
|
}
|
|
1567
1644
|
|
|
1568
1645
|
// collect all paths originating from this element that end up in an entity set
|
|
@@ -1576,7 +1653,11 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1576
1653
|
// drill into target only if
|
|
1577
1654
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1578
1655
|
// 2) current definition and target are the same (cycle)
|
|
1579
|
-
|
|
1656
|
+
// 3) it's no external reference
|
|
1657
|
+
if(!elt.$externalRef &&
|
|
1658
|
+
!elt._target.$hasEntitySet &&
|
|
1659
|
+
!elt._isToContainer &&
|
|
1660
|
+
curDef !== elt._target) {
|
|
1580
1661
|
// follow elements in the target but avoid cycles
|
|
1581
1662
|
setProp(elt, '$touched', true);
|
|
1582
1663
|
Object.values(elt._target.elements).forEach(e => npbs = npbs.concat(produceNavigationPath(e, elt._target)));
|
|
@@ -1586,16 +1667,18 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1586
1667
|
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
1587
1668
|
// last assoc step must not be to-n and target a singleton
|
|
1588
1669
|
let p = undefined;
|
|
1589
|
-
if (!elt
|
|
1590
|
-
|
|
1670
|
+
if (!elt.$externalRef &&
|
|
1671
|
+
!(edmUtils.isToMany(elt) &&
|
|
1672
|
+
edmUtils.isSingleton(elt._target) &&
|
|
1673
|
+
options.isV4())) {
|
|
1591
1674
|
if(elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1592
|
-
p = elt._target.$edmTgtPaths.find(p => p[0] === edmUtils.getBaseName(
|
|
1675
|
+
p = elt._target.$edmTgtPaths.find(p => p[0] === edmUtils.getBaseName(def.name)) || elt._target.$edmTgtPaths[0];
|
|
1593
1676
|
}
|
|
1594
1677
|
else if(elt._target.$hasEntitySet) {
|
|
1595
1678
|
const baseName = edmUtils.getBaseName(elt._target.$entitySetName || elt._target.name);
|
|
1596
1679
|
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1597
1680
|
// if target is in another schema, target the full qualified entity set
|
|
1598
|
-
p = (elt._target.$mySchemaName ===
|
|
1681
|
+
p = (elt._target.$mySchemaName === def.$mySchemaName) ?
|
|
1599
1682
|
[ baseName ] : [elt._target.$mySchemaName + '.EntityContainer', baseName];
|
|
1600
1683
|
}
|
|
1601
1684
|
if(p) {
|
|
@@ -1625,28 +1708,28 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1625
1708
|
}
|
|
1626
1709
|
}
|
|
1627
1710
|
|
|
1628
|
-
function determineEntitySet(
|
|
1711
|
+
function determineEntitySet(def) {
|
|
1629
1712
|
// if this is an entity or a view, determine if an entity set is required or not
|
|
1630
1713
|
// 1) must not be a proxy and not a containee in V4
|
|
1631
1714
|
// No annos are rendered for non-existing EntitySet targets.
|
|
1632
|
-
if(
|
|
1633
|
-
const hasEntitySet = isEntity(
|
|
1634
|
-
setProp(
|
|
1715
|
+
if(def.$hasEntitySet === undefined) {
|
|
1716
|
+
const hasEntitySet = isEntity(def) && !(options.isV4() && edmUtils.isContainee(def)) && !def.$proxy;
|
|
1717
|
+
setProp(def, '$hasEntitySet', hasEntitySet);
|
|
1635
1718
|
}
|
|
1636
1719
|
}
|
|
1637
1720
|
|
|
1638
|
-
function
|
|
1721
|
+
function initEdmTypesAndDescription(def) {
|
|
1639
1722
|
// 1. let all doc props become @Core.Descriptions
|
|
1640
1723
|
// 2. mark a member that will become a collection
|
|
1641
1724
|
// 3. assign the edm primitive type to elements, to be used in the rendering later
|
|
1642
|
-
assignAnnotation(
|
|
1643
|
-
markCollection(
|
|
1644
|
-
mapCdsToEdmProp(
|
|
1645
|
-
if (
|
|
1646
|
-
markCollection(
|
|
1647
|
-
mapCdsToEdmProp(
|
|
1648
|
-
}
|
|
1649
|
-
forEachMemberRecursively(
|
|
1725
|
+
assignAnnotation(def, '@Core.Description', def.doc);
|
|
1726
|
+
markCollection(def);
|
|
1727
|
+
mapCdsToEdmProp(def);
|
|
1728
|
+
if (def.returns) {
|
|
1729
|
+
markCollection(def.returns);
|
|
1730
|
+
mapCdsToEdmProp(def.returns);
|
|
1731
|
+
}
|
|
1732
|
+
forEachMemberRecursively(def,member => {
|
|
1650
1733
|
assignAnnotation(member, '@Core.Description', member.doc);
|
|
1651
1734
|
markCollection(member);
|
|
1652
1735
|
mapCdsToEdmProp(member);
|
|
@@ -1656,7 +1739,6 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1656
1739
|
mapCdsToEdmProp(member.returns);
|
|
1657
1740
|
}
|
|
1658
1741
|
});
|
|
1659
|
-
|
|
1660
1742
|
// mark members that need to be rendered as collections
|
|
1661
1743
|
function markCollection(obj) {
|
|
1662
1744
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
@@ -1673,19 +1755,37 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1673
1755
|
// Checks section starts here
|
|
1674
1756
|
//
|
|
1675
1757
|
|
|
1758
|
+
// eslint-disable-next-line no-unused-vars
|
|
1759
|
+
function resolveForeignKeyRefs() {
|
|
1760
|
+
forEachDefinition(csn, (def, defName) => {
|
|
1761
|
+
let currPath = ['definitions', defName ];
|
|
1762
|
+
forEachMemberRecursively(def, (construct, _constructName, _prop, path) => {
|
|
1763
|
+
if(construct.target && construct.keys) {
|
|
1764
|
+
construct.keys.forEach((fk, i) => {
|
|
1765
|
+
setProp(fk, '_artifact', csnUtils.inspectRef([...path, 'keys', i]).art);
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
}, currPath, true, { elementsOnly: true });
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
|
|
1676
1773
|
function inboundQualificationChecks() {
|
|
1677
|
-
forEachDefinition(csn, [ checkChainedArray ]);
|
|
1774
|
+
forEachDefinition(csn, [ attach$path, checkChainedArray ]);
|
|
1678
1775
|
checkNestedContextsAndServices();
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
//
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1776
|
+
throwWithAnyError();
|
|
1777
|
+
|
|
1778
|
+
// attach $path to all
|
|
1779
|
+
function attach$path(def, defName) {
|
|
1780
|
+
setProp(def, '$path', [ 'definitions', defName ]);
|
|
1781
|
+
forEachMemberRecursively(def,
|
|
1782
|
+
(member, _memberName, _prop, path) => {
|
|
1783
|
+
setProp(member, '$path', path);
|
|
1784
|
+
}, [ 'definitions', defName ]);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1687
1787
|
function checkChainedArray(def, defName) {
|
|
1688
|
-
if (!
|
|
1788
|
+
if (!isMyServiceRequested(defName))
|
|
1689
1789
|
return;
|
|
1690
1790
|
let currPath = ['definitions', defName];
|
|
1691
1791
|
checkIfItemsOfItems(def, undefined, undefined, currPath);
|
|
@@ -1695,13 +1795,13 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1695
1795
|
const constructType = csnUtils.effectiveType(construct);
|
|
1696
1796
|
if (constructType.items) {
|
|
1697
1797
|
if (constructType.items.items) {
|
|
1698
|
-
|
|
1798
|
+
message('chained-array-of', path);
|
|
1699
1799
|
return;
|
|
1700
1800
|
}
|
|
1701
1801
|
|
|
1702
1802
|
const itemsType = csnUtils.effectiveType(constructType.items);
|
|
1703
1803
|
if (itemsType.items)
|
|
1704
|
-
|
|
1804
|
+
message('chained-array-of', path);
|
|
1705
1805
|
}
|
|
1706
1806
|
}
|
|
1707
1807
|
}
|
|
@@ -1709,7 +1809,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1709
1809
|
function checkNestedContextsAndServices() {
|
|
1710
1810
|
!isBetaEnabled(options, 'nestedServices') && serviceRootNames.forEach(sn => {
|
|
1711
1811
|
const parent = whatsMyServiceRootName(sn, false);
|
|
1712
|
-
if(parent && parent !== sn) {
|
|
1812
|
+
if(parent && requestedServiceNames.includes(parent) && parent !== sn) {
|
|
1713
1813
|
message( 'service-nested-service', [ 'definitions', sn ], { art: parent },
|
|
1714
1814
|
'A service can\'t be nested within a service $(ART)' );
|
|
1715
1815
|
}
|
|
@@ -1718,7 +1818,7 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1718
1818
|
Object.entries(csn.definitions).forEach(([fqName, art]) => {
|
|
1719
1819
|
if(art.kind === 'context') {
|
|
1720
1820
|
const parent = whatsMyServiceRootName(fqName);
|
|
1721
|
-
if(parent) {
|
|
1821
|
+
if(requestedServiceNames.includes(parent)) {
|
|
1722
1822
|
message( 'service-nested-context', [ 'definitions', fqName ], { art: parent },
|
|
1723
1823
|
'A context can\'t be nested within a service $(ART)' );
|
|
1724
1824
|
}
|
|
@@ -1727,54 +1827,6 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1727
1827
|
}
|
|
1728
1828
|
}
|
|
1729
1829
|
|
|
1730
|
-
/**
|
|
1731
|
-
*
|
|
1732
|
-
* @param {String} identifier the illegal identifier
|
|
1733
|
-
* @param {CSN.Path} path
|
|
1734
|
-
*/
|
|
1735
|
-
function signalIllegalIdentifier(identifier, path) {
|
|
1736
|
-
error(null, path, { id: identifier },
|
|
1737
|
-
'OData identifier $(ID) must start with a letter or underscore, followed by at most 127 letters, underscores or digits'
|
|
1738
|
-
);
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
// { '#': this.csnUtils.isComposition(member.type) ? 'cmp' : 'std' },
|
|
1743
|
-
// {
|
|
1744
|
-
// std: 'An association can\'t have cardinality "to many" without an ON-condition',
|
|
1745
|
-
// cmp: 'A composition can\'t have cardinality "to many" without an ON-condition',
|
|
1746
|
-
// }
|
|
1747
|
-
|
|
1748
|
-
// Check the artifact identifier for compliance with the odata specification
|
|
1749
|
-
function checkArtifactIdentifierAndBoundActions(artifact) {
|
|
1750
|
-
if(artifact.$mySchemaName) {
|
|
1751
|
-
const artifactName = artifact.name.replace(`${artifact.$mySchemaName }.`, '');
|
|
1752
|
-
// if the artifact has bound actions, check the action identifiers and their param identifiers to be OData compliant
|
|
1753
|
-
if(artifact.actions) {
|
|
1754
|
-
Object.keys(artifact.actions).forEach(identifier => checkActionOrFunctionIdentifier(artifact.actions[identifier], identifier))
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
// if the artifact is an unbound function check it's identifer
|
|
1758
|
-
if(isActionOrFunction(artifact)){
|
|
1759
|
-
checkActionOrFunctionIdentifier(artifact, artifactName);
|
|
1760
|
-
} else if(![ 'service', 'context', 'event', 'aspect' ].includes(artifact.kind) && !isODataSimpleIdentifier(artifactName)) {
|
|
1761
|
-
signalIllegalIdentifier(artifactName, ['definitions', artifact.name]);
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
function checkActionOrFunctionIdentifier(actionOrFunction, actionOrFunctionName) {
|
|
1766
|
-
if(!isODataSimpleIdentifier(actionOrFunctionName)){
|
|
1767
|
-
signalIllegalIdentifier(actionOrFunctionName, actionOrFunction.$path);
|
|
1768
|
-
}
|
|
1769
|
-
if(actionOrFunction.params) {
|
|
1770
|
-
forEachGeneric(actionOrFunction, 'params', (param) => {
|
|
1771
|
-
if(!isODataSimpleIdentifier(param.name)){
|
|
1772
|
-
signalIllegalIdentifier(param.name, param.$path);
|
|
1773
|
-
}
|
|
1774
|
-
});
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
1830
|
//
|
|
1779
1831
|
// Checks Secition ends here
|
|
1780
1832
|
//
|
|
@@ -1863,7 +1915,29 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1863
1915
|
}, { });
|
|
1864
1916
|
// if dictionary has entries, add them to navPropEnty
|
|
1865
1917
|
if(Object.keys(o).length) {
|
|
1866
|
-
|
|
1918
|
+
// ReadRestrictions may have sub type ReadByKeyRestrictions { Description, LongDescription }
|
|
1919
|
+
// chop annotations into dictionaries
|
|
1920
|
+
if(prefix === '@Capabilities.ReadRestrictions' &&
|
|
1921
|
+
Object.keys(o).some(k => k.startsWith('ReadByKeyRestrictions.'))) {
|
|
1922
|
+
const no = {};
|
|
1923
|
+
Object.entries(o).forEach(([k,v]) => {
|
|
1924
|
+
const [head, ...tail] = k.split('.');
|
|
1925
|
+
if(head === 'ReadByKeyRestrictions' && tail.length) {
|
|
1926
|
+
if(!no['ReadByKeyRestrictions'])
|
|
1927
|
+
no['ReadByKeyRestrictions'] = {};
|
|
1928
|
+
// Don't try to add entry into non object
|
|
1929
|
+
if(typeof no['ReadByKeyRestrictions'] === 'object')
|
|
1930
|
+
no['ReadByKeyRestrictions'][tail.join('.')] = v;
|
|
1931
|
+
}
|
|
1932
|
+
else {
|
|
1933
|
+
no[k] = v;
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
navPropEntry[prop] = no;
|
|
1937
|
+
}
|
|
1938
|
+
else {
|
|
1939
|
+
navPropEntry[prop] = o;
|
|
1940
|
+
}
|
|
1867
1941
|
newEntry = true;
|
|
1868
1942
|
}
|
|
1869
1943
|
}
|
|
@@ -1885,21 +1959,17 @@ function initializeModel(csn, _options, messageFunctions)
|
|
|
1885
1959
|
|
|
1886
1960
|
function mapCdsToEdmProp(obj) {
|
|
1887
1961
|
if (obj.type && isBuiltinType(obj.type) && !isAssociationOrComposition(obj) && !obj.targetAspect) {
|
|
1888
|
-
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.
|
|
1962
|
+
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
1889
1963
|
assignProp(obj, '_edmType', edmType);
|
|
1890
|
-
} else if (obj._isCollection && (obj.items && isBuiltinType(getFinalTypeDef(obj.items.type)))) {
|
|
1891
|
-
let edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.
|
|
1964
|
+
} else if (obj._isCollection && (obj.items && isBuiltinType(csnUtils.getFinalTypeDef(obj.items.type)))) {
|
|
1965
|
+
let edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType'], obj.$path);
|
|
1892
1966
|
assignProp(obj, '_edmType', edmType);
|
|
1893
1967
|
}
|
|
1894
1968
|
// This is the special case when we have array of array, but will not be supported in the future
|
|
1895
|
-
else if (obj._isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(getFinalTypeDef(obj.items.items.type))) {
|
|
1896
|
-
let edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.
|
|
1969
|
+
else if (obj._isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(csnUtils.getFinalTypeDef(obj.items.items.type))) {
|
|
1970
|
+
let edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
1897
1971
|
assignProp(obj, '_edmType', edmType);
|
|
1898
1972
|
}
|
|
1899
|
-
|
|
1900
|
-
// check against the value of the @odata.Type annotation
|
|
1901
|
-
if (obj['@odata.Type'] && !['Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.String'].includes(obj['@odata.Type']))
|
|
1902
|
-
info(null, obj.$location, { type: obj['@odata.Type'] }, "@odata.Type: $(TYPE) is ignored, only Edm.String and Edm.Int[16,32,64] are allowed");
|
|
1903
1973
|
}
|
|
1904
1974
|
|
|
1905
1975
|
function ComputedDefaultValue(member) {
|
|
@@ -2286,4 +2356,5 @@ function assignProp(obj, prop, value) {
|
|
|
2286
2356
|
|
|
2287
2357
|
module.exports = {
|
|
2288
2358
|
initializeModel,
|
|
2359
|
+
assignAnnotation
|
|
2289
2360
|
}
|