@sap/cds-compiler 3.4.4 → 3.5.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 +58 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +58 -59
- package/lib/api/options.js +4 -2
- package/lib/api/validate.js +2 -2
- package/lib/base/cleanSymbols.js +2 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +6 -6
- package/lib/base/location.js +11 -12
- package/lib/base/message-registry.js +124 -28
- package/lib/base/messages.js +247 -179
- package/lib/base/model.js +14 -11
- package/lib/base/node-helpers.js +9 -10
- package/lib/base/optionProcessorHelper.js +138 -129
- package/lib/checks/actionsFunctions.js +5 -5
- package/lib/checks/annotationsOData.js +4 -4
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +3 -3
- package/lib/checks/defaultValues.js +3 -3
- package/lib/checks/elements.js +7 -7
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +4 -4
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/managedWithoutKeys.js +1 -1
- package/lib/checks/nonexpandableStructured.js +5 -3
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +5 -6
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -2
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +4 -4
- package/lib/checks/types.js +7 -7
- package/lib/checks/utils.js +4 -4
- package/lib/checks/validator.js +16 -13
- package/lib/compiler/.eslintrc.json +1 -1
- package/lib/compiler/assert-consistency.js +0 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +73 -15
- package/lib/compiler/define.js +3 -7
- package/lib/compiler/extend.js +212 -32
- package/lib/compiler/finalize-parse-cdl.js +7 -2
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +2 -5
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/shared.js +23 -12
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +6 -0
- package/lib/edm/annotations/genericTranslation.js +553 -319
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +88 -75
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmPreprocessor.js +106 -76
- package/lib/edm/edmUtils.js +41 -2
- package/lib/gen/Dictionary.json +34 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +66 -63
- package/lib/gen/language.tokens +81 -81
- package/lib/gen/languageLexer.interp +4 -10
- package/lib/gen/languageLexer.js +854 -869
- package/lib/gen/languageLexer.tokens +79 -81
- package/lib/gen/languageParser.js +14360 -14146
- package/lib/inspect/inspectModelStatistics.js +2 -2
- package/lib/inspect/inspectPropagation.js +6 -6
- package/lib/inspect/inspectUtils.js +2 -2
- package/lib/json/from-csn.js +82 -40
- package/lib/json/to-csn.js +82 -157
- package/lib/language/.eslintrc.json +1 -4
- package/lib/language/genericAntlrParser.js +59 -38
- package/lib/language/language.g4 +1508 -1490
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/main.js +3 -3
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/utils/filter.js +4 -3
- package/lib/optionProcessor.js +5 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +225 -159
- package/lib/render/toHdbcds.js +63 -63
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +55 -65
- package/lib/render/utils/common.js +20 -37
- package/lib/render/utils/delta.js +3 -3
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/assertUnique.js +13 -12
- package/lib/transform/db/associations.js +5 -5
- package/lib/transform/db/cdsPersistence.js +10 -8
- package/lib/transform/db/constraints.js +14 -14
- package/lib/transform/db/expansion.js +20 -22
- package/lib/transform/db/flattening.js +24 -42
- package/lib/transform/db/groupByOrderBy.js +3 -3
- package/lib/transform/db/temporal.js +6 -6
- package/lib/transform/db/transformExists.js +23 -23
- package/lib/transform/db/views.js +16 -16
- package/lib/transform/draft/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +12 -40
- package/lib/transform/forRelationalDB.js +17 -7
- package/lib/transform/localized.js +2 -2
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +106 -62
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +24 -19
- package/lib/transform/universalCsn/coreComputed.js +10 -10
- package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
- package/lib/transform/universalCsn/utils.js +3 -3
- package/lib/utils/file.js +5 -5
- package/lib/utils/moduleResolve.js +13 -13
- package/lib/utils/objectUtils.js +6 -6
- package/lib/utils/term.js +5 -2
- package/lib/utils/timetrace.js +51 -24
- package/package.json +5 -7
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/redirected-to-complex.md +4 -4
- package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
|
@@ -10,12 +10,12 @@ const { forEachDefinition } = require('../../model/csnUtils.js');
|
|
|
10
10
|
* options:
|
|
11
11
|
* v
|
|
12
12
|
*
|
|
13
|
-
* This module never produces errors. In case of "unexpected" situations we issue a
|
|
13
|
+
* This module never produces errors. In case of "unexpected" situations we issue a message and
|
|
14
14
|
* try to proceed with the processing as good as possible.
|
|
15
15
|
*
|
|
16
16
|
*/
|
|
17
17
|
function preprocessAnnotations(csn, serviceName, options) {
|
|
18
|
-
const {
|
|
18
|
+
const { message } = makeMessageFunction(csn, options);
|
|
19
19
|
let fkSeparator = '_';
|
|
20
20
|
|
|
21
21
|
resolveShortcuts();
|
|
@@ -32,7 +32,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// return value can be null is target has no key
|
|
35
|
-
function getKeyOfTargetOfManagedAssoc(assoc) {
|
|
35
|
+
function getKeyOfTargetOfManagedAssoc(anno, assoc) {
|
|
36
36
|
// assoc.target can be the name of the target or the object itself
|
|
37
37
|
let targetName = (typeof assoc.target === 'object') ? assoc.target.name : assoc.target;
|
|
38
38
|
let target = (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
|
|
@@ -40,10 +40,11 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
40
40
|
let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
|
|
41
41
|
if (keyNames.length === 0) {
|
|
42
42
|
keyNames.push('MISSING');
|
|
43
|
-
|
|
43
|
+
message('odata-anno-preproc', null, { anno, name: targetName, '#': 'nokey' },
|
|
44
|
+
'target $(NAME) has no key');
|
|
44
45
|
}
|
|
45
46
|
else if (keyNames.length > 1)
|
|
46
|
-
|
|
47
|
+
message('odata-anno-preproc', null, { anno, name: targetName, '#': 'multkeys' });
|
|
47
48
|
|
|
48
49
|
return keyNames[0];
|
|
49
50
|
}
|
|
@@ -58,32 +59,29 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
58
59
|
let art = null;
|
|
59
60
|
|
|
60
61
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
62
|
+
const location = [ 'definitions', artifactName ];
|
|
61
63
|
if(artifactName === serviceName || artifactName.startsWith(serviceName + '.')) {
|
|
62
64
|
art = artifactName;
|
|
63
|
-
handleAnnotations(artifactName, artifact);
|
|
65
|
+
handleAnnotations(artifactName, artifact, location);
|
|
64
66
|
artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
|
|
65
|
-
handleAnnotations(elementName, element);
|
|
67
|
+
handleAnnotations(elementName, element, [ ...location, 'elements', elementName ]);
|
|
66
68
|
});
|
|
67
69
|
artifact.actions && Object.values(artifact.actions).forEach(action => {
|
|
68
70
|
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
69
|
-
handleAnnotations(paramName, param);
|
|
71
|
+
handleAnnotations(paramName, param, [ ...location, 'actions', action, 'params', paramName ]);
|
|
70
72
|
});
|
|
71
73
|
});
|
|
72
74
|
}
|
|
73
75
|
});
|
|
74
76
|
|
|
75
|
-
function handleAnnotations(carrierName, carrier) {
|
|
77
|
+
function handleAnnotations(carrierName, carrier, location) {
|
|
76
78
|
|
|
77
79
|
// collect the names of the carrier's annotation properties
|
|
78
|
-
let annoNames = Object.keys(carrier).filter( x => x
|
|
80
|
+
let annoNames = Object.keys(carrier).filter( x => x[0] === '@')
|
|
79
81
|
|
|
80
82
|
for (let aName of annoNames) {
|
|
81
83
|
let aNameWithoutQualifier = aName.split('#')[0];
|
|
82
84
|
|
|
83
|
-
//for warning messages
|
|
84
|
-
let ctx = 'target: ' + art + '/' + carrierName;
|
|
85
|
-
|
|
86
|
-
|
|
87
85
|
// Always - draft annotations, value is action name
|
|
88
86
|
// - v2: prefix with entity name
|
|
89
87
|
// - prefix with service name
|
|
@@ -91,11 +89,11 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
91
89
|
|
|
92
90
|
// Always - FixedValueListShortcut
|
|
93
91
|
// expand shortcut form of ValueList annotation
|
|
94
|
-
fixedValueListShortCut(carrier, aNameWithoutQualifier
|
|
92
|
+
fixedValueListShortCut(carrier, aNameWithoutQualifier);
|
|
95
93
|
|
|
96
94
|
// Always - TextArrangementReordering
|
|
97
95
|
// convert @Common.TextArrangement annotation that is on same level as Text annotation into a nested annotation
|
|
98
|
-
textArrangementReordering(carrier, aName, aNameWithoutQualifier
|
|
96
|
+
textArrangementReordering(carrier, aName, aNameWithoutQualifier);
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
// inner functions
|
|
@@ -121,9 +119,9 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
121
119
|
}
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
function fixedValueListShortCut(carrier,
|
|
125
|
-
if (
|
|
126
|
-
|
|
122
|
+
function fixedValueListShortCut(carrier, anno) {
|
|
123
|
+
if (anno === '@Common.ValueList.entity' ||
|
|
124
|
+
anno === '@Common.ValueList.viaAssociation') {
|
|
127
125
|
|
|
128
126
|
const _fixedValueListShortCut = () => {
|
|
129
127
|
// note: we loop over all annotations that were originally present, even if they are
|
|
@@ -136,7 +134,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
136
134
|
}
|
|
137
135
|
|
|
138
136
|
if (carrier.kind === 'entity') {
|
|
139
|
-
|
|
137
|
+
message('odata-anno-preproc', [...location, anno], { anno, '#': 'notforentity' });
|
|
140
138
|
return false;
|
|
141
139
|
}
|
|
142
140
|
|
|
@@ -146,32 +144,37 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
146
144
|
let enameShort = null; // (string) name of value list entity, short (i.e. name within service)
|
|
147
145
|
let enameFull = null; // (string) name of value list entity, fully qualified name
|
|
148
146
|
|
|
149
|
-
if (
|
|
147
|
+
if (anno === '@Common.ValueList.viaAssociation') {
|
|
150
148
|
// value is expected to be an expression, namely the path to an association of the carrier entity
|
|
151
149
|
let assocName = carrier['@Common.ValueList.viaAssociation']['='];
|
|
152
150
|
if (!assocName) {
|
|
153
|
-
|
|
151
|
+
message('odata-anno-preproc', [...location, anno], { anno, '#': 'viaassoc' });
|
|
154
152
|
return false;
|
|
155
153
|
}
|
|
156
154
|
let assoc = csn.definitions[art].elements[assocName];
|
|
157
155
|
if (!assoc || !assoc.target) {
|
|
158
|
-
|
|
156
|
+
message('odata-anno-preproc', [...location, anno], { anno, id: assocName, '#': 'noassoc' });
|
|
159
157
|
return false;
|
|
160
158
|
}
|
|
161
159
|
|
|
162
160
|
enameFull = assoc.target.name || assoc.target; // full name
|
|
163
161
|
enameShort = enameFull.split('.').pop();
|
|
164
162
|
}
|
|
165
|
-
else if (
|
|
163
|
+
else if (anno === '@Common.ValueList.entity') {
|
|
166
164
|
// if both annotations are present, ignore 'entity' and raise a message
|
|
167
165
|
if (annoNames.map(x=>x.split('#')[0]).find(x=>(x==='@Common.ValueList.viaAssociation'))) {
|
|
168
|
-
|
|
166
|
+
message('odata-anno-preproc', [...location, anno],
|
|
167
|
+
{
|
|
168
|
+
name: '@Common.ValueList.entity', anno: '@Common.ValueList',
|
|
169
|
+
value: 'entity', code: 'viaAssociation', '#': 'vallistignored'
|
|
170
|
+
});
|
|
169
171
|
return false;
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
let annoVal = carrier['@Common.ValueList.entity']; // name of value list entity
|
|
173
175
|
if (annoVal['=']) {
|
|
174
|
-
|
|
176
|
+
message('odata-anno-preproc', [...location, anno], { anno, '#': 'notastring' },
|
|
177
|
+
);
|
|
175
178
|
}
|
|
176
179
|
|
|
177
180
|
let nameprefix = art.replace(/.[^.]+$/, ''); // better way of getting the service name?
|
|
@@ -182,7 +185,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
182
185
|
|
|
183
186
|
let vlEntity = csn.definitions[enameFull]; // (object) value list entity
|
|
184
187
|
if (!vlEntity) {
|
|
185
|
-
|
|
188
|
+
message('odata-anno-preproc', [...location, anno ], { anno, id: enameFull, '#': 'notexist' });
|
|
186
189
|
return false;
|
|
187
190
|
}
|
|
188
191
|
|
|
@@ -196,7 +199,8 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
196
199
|
// if this is a managed assoc, use fk field instead (if there is a single one)
|
|
197
200
|
let localDataProp = carrierName.split('/').pop();
|
|
198
201
|
if (carrier.target && carrier.on === undefined) {
|
|
199
|
-
localDataProp = localDataProp + fkSeparator +
|
|
202
|
+
localDataProp = localDataProp + fkSeparator +
|
|
203
|
+
getKeyOfTargetOfManagedAssoc(anno, carrier);
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
// if this carrier is a generated foreign key field and the association is marked @cds.api.ignore
|
|
@@ -209,15 +213,15 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
209
213
|
}
|
|
210
214
|
|
|
211
215
|
// valueListProp: the (single) key field of the value list entity
|
|
212
|
-
// if no key or multiple keys ->
|
|
216
|
+
// if no key or multiple keys -> message
|
|
213
217
|
let valueListProp = null;
|
|
214
218
|
let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key && !vlEntity.elements[x].target );
|
|
215
219
|
if (keys.length === 0) {
|
|
216
|
-
|
|
220
|
+
message('odata-anno-preproc', [...location, anno], { anno, name: enameFull, '#': 'vhlnokey' });
|
|
217
221
|
return false;
|
|
218
222
|
}
|
|
219
223
|
else if (keys.length > 1)
|
|
220
|
-
|
|
224
|
+
message('odata-anno-preproc', [...location, anno], { anno, name: enameFull, '#': 'vhlmultkeys' });
|
|
221
225
|
valueListProp = keys[0];
|
|
222
226
|
|
|
223
227
|
// textField:
|
|
@@ -278,20 +282,20 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
278
282
|
|
|
279
283
|
const success = _fixedValueListShortCut();
|
|
280
284
|
if (!success) {
|
|
281
|
-
// In case of failure, avoid subsequent
|
|
282
|
-
delete carrier[
|
|
285
|
+
// In case of failure, avoid subsequent messages
|
|
286
|
+
delete carrier[anno];
|
|
283
287
|
delete carrier['@Common.ValueList.type'];
|
|
284
288
|
}
|
|
285
289
|
}
|
|
286
290
|
}
|
|
287
291
|
|
|
288
|
-
function textArrangementReordering(carrier, aName, aNameWithoutQualifier
|
|
292
|
+
function textArrangementReordering(carrier, aName, aNameWithoutQualifier) {
|
|
289
293
|
if (aNameWithoutQualifier === '@Common.TextArrangement') {
|
|
290
294
|
let value = carrier[aName];
|
|
291
295
|
let textAnno = carrier['@Common.Text'];
|
|
292
296
|
// can only occur if there is a @Common.Text annotation at the same target
|
|
293
297
|
if (!textAnno) {
|
|
294
|
-
|
|
298
|
+
message('odata-anno-preproc', [...location, '@Common.TextArrangement'], { anno: '@Common.TextArrangement', name: '@Common.Text', '#': 'txtarr' });
|
|
295
299
|
}
|
|
296
300
|
|
|
297
301
|
//change the scalar anno into a "pseudo-structured" one
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -9,7 +9,7 @@ const VALUELIST_NAVPROP_PREFIX = '';
|
|
|
9
9
|
const edmUtils = require('./edmUtils.js')
|
|
10
10
|
const { initializeModel } = require('./edmPreprocessor.js');
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
|
-
const { setProp } = require('../base/model');
|
|
12
|
+
const { setProp, isBetaEnabled } = require('../base/model');
|
|
13
13
|
const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
|
|
14
14
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
15
15
|
const { makeMessageFunction } = require('../base/messages');
|
|
@@ -43,7 +43,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
43
43
|
setProp(csn.meta, 'options', _csn.meta.options);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const [
|
|
46
|
+
const [
|
|
47
|
+
allServices,
|
|
47
48
|
allSchemas,
|
|
48
49
|
reqDefs,
|
|
49
50
|
whatsMyServiceRootName,
|
|
@@ -133,11 +134,18 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
133
134
|
-----------------------------------------------*/
|
|
134
135
|
let LeadSchema;
|
|
135
136
|
const fqSchemaXRef = [serviceCsn.name];
|
|
136
|
-
const whatsMySchemaName = function(n
|
|
137
|
-
return
|
|
137
|
+
const whatsMySchemaName = function(n) {
|
|
138
|
+
return fqSchemaXRef.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') ? sn : rc, undefined);
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
// tunnel schema xref and servicename in options to edm.Typebase to rectify
|
|
142
|
+
// type references that are eventually also prefixed with the service schema name.
|
|
143
|
+
options.serviceName = serviceCsn.name;
|
|
144
|
+
// List of all schema names in this service, including the service itself
|
|
145
|
+
options.whatsMySchemaName = whatsMySchemaName;
|
|
146
|
+
options.whatsMyServiceRootName = whatsMyServiceRootName;
|
|
147
|
+
|
|
148
|
+
let xServiceRefs = {};
|
|
141
149
|
const UsedTypes = {};
|
|
142
150
|
function collectUsedType(csn, typeName = (csn.items?.type || csn.type)) {
|
|
143
151
|
if(typeName) {
|
|
@@ -160,26 +168,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
160
168
|
};
|
|
161
169
|
|
|
162
170
|
if(options.isV4()) {
|
|
163
|
-
// tunnel schema xref and servicename in options to edm.Typebase to rectify
|
|
164
|
-
// type references that are eventually also prefixed with the service schema name.
|
|
165
|
-
options.serviceName = serviceCsn.name;
|
|
166
|
-
// List of all schema names in this service, including the service itself
|
|
167
|
-
options.whatsMySchemaName = whatsMySchemaName;
|
|
168
|
-
|
|
169
171
|
// Add additional schema containers as sub contexts to the service
|
|
170
172
|
Object.entries(allSchemas).forEach(([fqName, art]) => {
|
|
171
173
|
if(serviceCsn.name === whatsMyServiceRootName(fqName) &&
|
|
172
|
-
fqName.startsWith(serviceCsn.name + '.')
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
name
|
|
178
|
-
fqName,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
174
|
+
fqName.startsWith(serviceCsn.name + '.')) {
|
|
175
|
+
if(art.kind === 'reference')
|
|
176
|
+
fqSchemaXRef.push(fqName);
|
|
177
|
+
if(art.kind === 'schema') {
|
|
178
|
+
fqSchemaXRef.push(fqName);
|
|
179
|
+
// Strip the toplevel service schema name (see comment above)
|
|
180
|
+
const name = fqName.replace(serviceCsn.name + '.', '');
|
|
181
|
+
subSchemaDictionary[name] = {
|
|
182
|
+
name,
|
|
183
|
+
fqName,
|
|
184
|
+
_csn: art,
|
|
185
|
+
container: false,
|
|
186
|
+
definitions: Object.create(null)
|
|
187
|
+
};
|
|
188
|
+
}
|
|
183
189
|
}
|
|
184
190
|
}, subSchemaDictionary);
|
|
185
191
|
|
|
@@ -203,13 +209,6 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
203
209
|
const schema = subSchemaDictionary[name];
|
|
204
210
|
service.registerSchema(schema.fqName, createSchema(schema));
|
|
205
211
|
});
|
|
206
|
-
|
|
207
|
-
// Add cross service references to the EDM
|
|
208
|
-
xServiceRefs.forEach(ref => {
|
|
209
|
-
let r = new Edm.Reference(v, ref.ref);
|
|
210
|
-
r.append(new Edm.Include(v, ref.inc))
|
|
211
|
-
edm._defaultRefs.push(r);
|
|
212
|
-
});
|
|
213
212
|
}
|
|
214
213
|
else {
|
|
215
214
|
populateSchemas(subSchemaDictionary);
|
|
@@ -231,40 +230,43 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
231
230
|
}
|
|
232
231
|
}));
|
|
233
232
|
});
|
|
234
|
-
// Create annotations and distribute into Schemas
|
|
235
|
-
addAnnotations();
|
|
233
|
+
// Create annotations and distribute into Schemas, merge vocabulary cross refs into xServiceRefs
|
|
234
|
+
addAnnotations(xServiceRefs);
|
|
235
|
+
|
|
236
|
+
// Finally add cross service references into the EDM and extract the targetSchemaNames
|
|
237
|
+
// for the type cross check
|
|
238
|
+
Object.values(xServiceRefs).forEach(ref => {
|
|
239
|
+
let r = new Edm.Reference(v, ref.ref);
|
|
240
|
+
r.append(new Edm.Include(v, ref.inc))
|
|
241
|
+
edm._defaultRefs.push(r);
|
|
242
|
+
});
|
|
236
243
|
|
|
237
|
-
// type cross check
|
|
238
|
-
const xServiceRefNames = xServiceRefs.map(r => r.name).sort((a,b)=>b.length-a.length);
|
|
239
244
|
for(let typeName in UsedTypes) {
|
|
240
245
|
if(!isBuiltinType(typeName)) {
|
|
241
246
|
let iTypeName = typeName;
|
|
242
247
|
/*
|
|
243
248
|
Report type ref, if the type is
|
|
244
249
|
- not a builtin,
|
|
245
|
-
- not included in required definitions
|
|
250
|
+
- not included in required definitions
|
|
246
251
|
- not a type clash (reported in type exposure),
|
|
247
252
|
- a @cds.external service member but can't be rendered
|
|
248
|
-
- and not a cross referenced type
|
|
249
253
|
*/
|
|
250
254
|
if(!typeName.startsWith(serviceCsn.name + '.'))
|
|
251
255
|
iTypeName = serviceCsn.name + '.' + typeName;
|
|
252
256
|
const def = reqDefs.definitions[iTypeName];
|
|
253
257
|
|
|
254
258
|
const usages = UsedTypes[typeName].filter(u => !u.$NameClashReported);
|
|
255
|
-
if(usages.length > 0) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
message('odata-spec-violation-type', usages[0].$location,
|
|
267
|
-
{ type: typeName, name: serviceCsn.name, '#': 'missing' } );
|
|
259
|
+
if(usages.length > 0 && def && !def.$isRendered && def['@cds.external']) {
|
|
260
|
+
message('odata-spec-violation-type', usages[0].$location,
|
|
261
|
+
{
|
|
262
|
+
type: typeName,
|
|
263
|
+
anno: '@cds.external',
|
|
264
|
+
name: serviceCsn.name,
|
|
265
|
+
code: def.elements ? 'Edm.ComplexType' : 'Edm.TypeDefinition',
|
|
266
|
+
version: options.isV4() ? '4.0' : '2.0',
|
|
267
|
+
'#': 'external'
|
|
268
|
+
}
|
|
269
|
+
);
|
|
268
270
|
}
|
|
269
271
|
}
|
|
270
272
|
}
|
|
@@ -328,11 +330,13 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
328
330
|
|
|
329
331
|
return Object.entries(allSchemas).reduce((references, [fqName, art]) => {
|
|
330
332
|
// add references
|
|
331
|
-
if(art.kind === 'reference' &&
|
|
332
|
-
|
|
333
|
+
if(art.kind === 'reference' &&
|
|
334
|
+
whatsMySchemaName(fqName) &&
|
|
335
|
+
serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
|
|
336
|
+
references[art.inc.Namespace] = art;
|
|
333
337
|
}
|
|
334
338
|
return references;
|
|
335
|
-
},
|
|
339
|
+
}, {});
|
|
336
340
|
}
|
|
337
341
|
|
|
338
342
|
// Main schema creator function
|
|
@@ -385,12 +389,18 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
385
389
|
{
|
|
386
390
|
edmUtils.foreach(schemaCsn.definitions,
|
|
387
391
|
artifact => edmUtils.isDerivedType(artifact) &&
|
|
388
|
-
!artifact.target
|
|
392
|
+
!artifact.target&&
|
|
389
393
|
artifact.name.startsWith(schemaNamePrefix),
|
|
390
394
|
[createTypeDefinitionV4, markRendered]);
|
|
391
395
|
}
|
|
392
396
|
|
|
393
|
-
|
|
397
|
+
if(isBetaEnabled(options, 'odataTerms')) {
|
|
398
|
+
edmUtils.foreach(schemaCsn.definitions,
|
|
399
|
+
a => a.kind === 'annotation' && a.name.startsWith(schemaNamePrefix),
|
|
400
|
+
createTerm);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// fetch all exising children names in a map
|
|
394
404
|
const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
|
|
395
405
|
const name = cur._edmAttributes.Name;
|
|
396
406
|
if(acc[name] === undefined) {
|
|
@@ -412,12 +422,6 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
412
422
|
}
|
|
413
423
|
});
|
|
414
424
|
|
|
415
|
-
if(Schema._children.length === 0 ||
|
|
416
|
-
(Schema._children.length === 1 && Schema._children[0].kind === 'EntityContainer')) {
|
|
417
|
-
// FIXME: Location for sub schemas?
|
|
418
|
-
warning(null, ['definitions', Schema._edmAttributes.Namespace], { name: Schema._edmAttributes.Namespace }, 'Schema $(NAME) is empty');
|
|
419
|
-
}
|
|
420
|
-
|
|
421
425
|
Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
|
|
422
426
|
if(refs.length > 1) {
|
|
423
427
|
error(null, ['definitions', `${Schema._edmAttributes.Namespace}.${name}`], { name: Schema._edmAttributes.Namespace },
|
|
@@ -448,7 +452,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
448
452
|
const pLoc = [...loc, 'elements', p._edmAttributes.Name];
|
|
449
453
|
edmTypeCompatibilityCheck(p, pLoc);
|
|
450
454
|
if(p._edmAttributes.Name === EntityTypeName)
|
|
451
|
-
message('odata-spec-violation-property-name', pLoc, {
|
|
455
|
+
message('odata-spec-violation-property-name', pLoc, { meta: entityCsn.kind });
|
|
452
456
|
|
|
453
457
|
if(options.isV2() && p._isCollection && !p._csn.target)
|
|
454
458
|
message('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
@@ -524,7 +528,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
524
528
|
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p._edmAttributes.Name ];
|
|
525
529
|
edmTypeCompatibilityCheck(p, pLoc);
|
|
526
530
|
if(p._edmAttributes.Name === complexType._edmAttributes.Name)
|
|
527
|
-
message('odata-spec-violation-property-name', pLoc, {
|
|
531
|
+
message('odata-spec-violation-property-name', pLoc, { meta: structuredTypeCsn.kind });
|
|
528
532
|
|
|
529
533
|
if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
|
|
530
534
|
message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
|
|
@@ -618,6 +622,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
618
622
|
return [ props, hasStream ];
|
|
619
623
|
}
|
|
620
624
|
|
|
625
|
+
function createTerm(termCsn) {
|
|
626
|
+
const attributes = { Name: termCsn.name.replace(schemaNamePrefix, '') };
|
|
627
|
+
const term = new Edm.Term(v, attributes, termCsn);
|
|
628
|
+
Schema.append(term);
|
|
629
|
+
}
|
|
630
|
+
|
|
621
631
|
// V4 <TypeDefintion>
|
|
622
632
|
function createTypeDefinitionV4(typeCsn)
|
|
623
633
|
{
|
|
@@ -970,8 +980,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
970
980
|
}
|
|
971
981
|
|
|
972
982
|
// generate the Edm.Annotations tree and append it to the corresponding schema
|
|
973
|
-
function addAnnotations() {
|
|
974
|
-
let { annos, usedVocabularies } = translate.csn2annotationEdm(reqDefs, serviceCsn.name, Edm, options, messageFunctions);
|
|
983
|
+
function addAnnotations(xServiceRefs) {
|
|
984
|
+
let { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions);
|
|
975
985
|
// distribute edm:Annotations into the schemas
|
|
976
986
|
// Distribute each anno into Schema
|
|
977
987
|
annos.forEach(anno => {
|
|
@@ -979,21 +989,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
979
989
|
// if no target schema has been found, it's a service annotation that applies to the service schema
|
|
980
990
|
if(targetSchema === undefined)
|
|
981
991
|
targetSchema = serviceCsn.name;
|
|
982
|
-
if(targetSchema) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
anno.setEdmAttribute('Target', newTarget);
|
|
986
|
-
}
|
|
987
|
-
edm._service._schemas[targetSchema]._annotations.push(anno);
|
|
992
|
+
if(targetSchema !== serviceCsn.name) {
|
|
993
|
+
const newTarget = anno._edmAttributes.Target.replace(serviceCsn.name + '.', '');
|
|
994
|
+
anno.setEdmAttribute('Target', newTarget);
|
|
988
995
|
}
|
|
996
|
+
edm._service._schemas[targetSchema]._annotations.push(anno);
|
|
989
997
|
});
|
|
990
998
|
annos = [];
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
999
|
+
|
|
1000
|
+
// create service cross reference and merge it into xServiceRefs
|
|
1001
|
+
xrefs.forEach(xr => {
|
|
1002
|
+
if(xr !== serviceCsn.name) {
|
|
1003
|
+
const art = edmUtils.createSchemaRef(allServices, xr);
|
|
1004
|
+
if(xServiceRefs[art.inc.Namespace] === undefined)
|
|
1005
|
+
xServiceRefs[art.inc.Namespace] = art;
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
// merge vocabulary cross references into xServiceRefs
|
|
1009
|
+
usedVocabularies.forEach(art => xServiceRefs[art.inc.Namespace] = art);
|
|
997
1010
|
}
|
|
998
1011
|
|
|
999
1012
|
function edmTypeCompatibilityCheck(p, pLoc) {
|
package/lib/edm/edm.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const edmUtils = require('./edmUtils.js');
|
|
4
4
|
const { isBuiltinType } = require('../model/csnUtils.js');
|
|
5
|
-
const { forEach } = require(
|
|
5
|
+
const { forEach } = require('../utils/objectUtils');
|
|
6
6
|
const { isBetaEnabled } = require('../base/model.js');
|
|
7
7
|
|
|
8
8
|
// facet definitions, optional could either be true or array of edm types
|
|
@@ -722,8 +722,8 @@ function getEdm(options, messageFunctions) {
|
|
|
722
722
|
}
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
-
// Set the collection property if this is either an element or a
|
|
726
|
-
this._isCollection = (csn.kind === undefined) ? csn._isCollection : false;
|
|
725
|
+
// Set the collection property if this is either an element, parameter or a term def
|
|
726
|
+
this._isCollection = (csn.kind === undefined || csn.kind === 'annotation') ? csn._isCollection : false;
|
|
727
727
|
|
|
728
728
|
if(options.whatsMySchemaName && this._edmAttributes[typeName]) {
|
|
729
729
|
let schemaName = options.whatsMySchemaName(this._edmAttributes[typeName]);
|
|
@@ -834,6 +834,18 @@ function getEdm(options, messageFunctions) {
|
|
|
834
834
|
}
|
|
835
835
|
}
|
|
836
836
|
|
|
837
|
+
class Term extends TypeBase
|
|
838
|
+
{
|
|
839
|
+
constructor(v, attributes, csn)
|
|
840
|
+
{
|
|
841
|
+
super(v, attributes, csn);
|
|
842
|
+
const appliesTo = csn['@odata.term.AppliesTo'];
|
|
843
|
+
if(appliesTo) {
|
|
844
|
+
this.setEdmAttribute('AppliesTo', Array.isArray(appliesTo) ? appliesTo.map(v=>v['=']||v).join(' ') : appliesTo['='] || appliesTo);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
837
849
|
class TypeDefinition extends TypeBase
|
|
838
850
|
{
|
|
839
851
|
constructor(v, attributes, csn)
|
|
@@ -1639,6 +1651,8 @@ function getEdm(options, messageFunctions) {
|
|
|
1639
1651
|
EntityContainer,
|
|
1640
1652
|
EntitySet,
|
|
1641
1653
|
Singleton,
|
|
1654
|
+
TypeBase,
|
|
1655
|
+
Term,
|
|
1642
1656
|
TypeDefinition,
|
|
1643
1657
|
EnumType,
|
|
1644
1658
|
ComplexType,
|
|
@@ -108,8 +108,8 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
108
108
|
let CommonAttributes = element[prop];
|
|
109
109
|
if(!Array.isArray(CommonAttributes)) {
|
|
110
110
|
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
111
|
-
{ anno: '@Common.
|
|
112
|
-
'
|
|
111
|
+
{ anno: '@Common.Attributes', code: JSON.stringify(CommonAttributes) },
|
|
112
|
+
'Expecting array value for $(ANNO): $(CODE)');
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -125,7 +125,7 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
125
125
|
if(!Array.isArray(ContextDefiningProperties)) {
|
|
126
126
|
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
127
127
|
{ anno: '@Aggregation.ContextDefiningProperties', code: JSON.stringify(ContextDefiningProperties) },
|
|
128
|
-
'
|
|
128
|
+
'Expecting array value for $(ANNO): $(CODE)');
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
if(ContextDefiningProperties.length > 0)
|
|
@@ -198,7 +198,7 @@ function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error
|
|
|
198
198
|
error(null, ['definitions', struct.name ],
|
|
199
199
|
{ anno: '@Capabilities.FilterRestrictions.FilterExpressionRestrictions',
|
|
200
200
|
code: JSON.stringify(filterRestrictions) },
|
|
201
|
-
'
|
|
201
|
+
'Expected array value for $(ANNO): $(CODE)');
|
|
202
202
|
return;
|
|
203
203
|
}
|
|
204
204
|
filterRestrictions.forEach(v => {
|
|
@@ -215,7 +215,7 @@ function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error
|
|
|
215
215
|
error(null, ['definitions', struct.name],
|
|
216
216
|
{ anno: '@Capabilities.FilterRestrictions.RequiredProperties',
|
|
217
217
|
code: JSON.stringify(requiredProperties) },
|
|
218
|
-
'
|
|
218
|
+
'Expecting array value for $(ANNO): $(CODE)');
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
221
|
|