@sap/cds-compiler 3.4.2 → 3.5.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 +80 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +15 -16
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +2 -2
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +61 -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 +177 -58
- package/lib/base/messages.js +252 -180
- 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/.eslintrc.json +2 -0
- 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 +4 -1
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/builtins.js +14 -14
- package/lib/compiler/checks.js +123 -48
- package/lib/compiler/define.js +12 -13
- package/lib/compiler/extend.js +266 -60
- package/lib/compiler/finalize-parse-cdl.js +10 -5
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +14 -6
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +27 -16
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +20 -0
- package/lib/edm/annotations/genericTranslation.js +604 -358
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +275 -222
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +6 -6
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +107 -77
- package/lib/edm/edmUtils.js +44 -5
- package/lib/gen/Dictionary.json +210 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +67 -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 +14309 -13832
- 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 +102 -55
- package/lib/json/to-csn.js +119 -198
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +113 -133
- package/lib/language/language.g4 +1550 -1506
- package/lib/language/multiLineStringParser.js +3 -3
- package/lib/language/textUtils.js +2 -2
- package/lib/main.js +3 -3
- package/lib/model/csnRefs.js +5 -0
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +100 -0
- package/lib/optionProcessor.js +5 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +311 -276
- package/lib/render/toHdbcds.js +97 -94
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +127 -223
- package/lib/render/utils/common.js +141 -108
- package/lib/render/utils/delta.js +227 -0
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- package/lib/transform/db/.eslintrc.json +2 -0
- 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/.eslintrc.json +1 -35
- package/lib/transform/draft/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +8 -29
- package/lib/transform/forRelationalDB.js +16 -6
- package/lib/transform/localized.js +11 -10
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +113 -47
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +17 -10
- 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 -8
- 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
- package/lib/modelCompare/filter.js +0 -83
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
|
|
|
@@ -346,4 +346,4 @@ module.exports = {
|
|
|
346
346
|
setSAPSpecificV2AnnotationsToEntityContainer,
|
|
347
347
|
setSAPSpecificV2AnnotationsToEntitySet,
|
|
348
348
|
setSAPSpecificV2AnnotationsToAssociation
|
|
349
|
-
};
|
|
349
|
+
};
|
|
@@ -24,11 +24,11 @@ function resolveForeignKeyRefs(csn) {
|
|
|
24
24
|
function inboundQualificationChecks(csn, options, messageFunctions,
|
|
25
25
|
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName) {
|
|
26
26
|
const csnUtils = getUtils(csn);
|
|
27
|
-
const { message,
|
|
27
|
+
const { message, throwWithError } = messageFunctions;
|
|
28
28
|
|
|
29
29
|
forEachDefinition(csn, [ attach$path, checkChainedArray ]);
|
|
30
30
|
checkNestedContextsAndServices();
|
|
31
|
-
|
|
31
|
+
throwWithError();
|
|
32
32
|
|
|
33
33
|
// attach $path to all
|
|
34
34
|
function attach$path(def, defName) {
|
|
@@ -18,7 +18,7 @@ const NavResAnno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
|
|
|
18
18
|
const capabilities = Object.keys(require('../gen/Dictionary.json').
|
|
19
19
|
types['Capabilities.NavigationPropertyRestriction'].Properties).
|
|
20
20
|
filter(c => !['NavigationProperty', 'Navigability'].includes(c)).
|
|
21
|
-
map(c =>
|
|
21
|
+
map(c => '@Capabilities.'+c);
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* edmPreprocessor warms up the model so that it can be converted into an EDM document and
|
|
@@ -128,6 +128,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
128
128
|
},
|
|
129
129
|
linkAssociationTarget
|
|
130
130
|
]);
|
|
131
|
+
forEachGeneric(csn, 'vocabularies', (term, termName) => {
|
|
132
|
+
const mySchemaName = whatsMySchemaName(termName);
|
|
133
|
+
mySchemaName && setProp(term, '$mySchemaName', mySchemaName);
|
|
134
|
+
});
|
|
131
135
|
// initialize requested services
|
|
132
136
|
const skip = { skipArtifact: (_def, defName) => !isMyServiceRequested(defName) };
|
|
133
137
|
forEachDefinition({ definitions: serviceRoots }, initService, skip);
|
|
@@ -161,7 +165,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
161
165
|
if(options.isV4())
|
|
162
166
|
forEachDefinition(reqDefs, [
|
|
163
167
|
initEdmNavPropBindingTargets,
|
|
164
|
-
|
|
168
|
+
pullupCapabilitiesAnnotations,
|
|
165
169
|
annotateOptionalActFuncParams
|
|
166
170
|
]);
|
|
167
171
|
|
|
@@ -172,7 +176,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
172
176
|
forEachDefinition(reqDefs, [
|
|
173
177
|
initEdmKeyRefPaths,
|
|
174
178
|
initEdmNavPropBindingPaths,
|
|
175
|
-
|
|
179
|
+
finalize
|
|
176
180
|
]);
|
|
177
181
|
}
|
|
178
182
|
return [
|
|
@@ -264,7 +268,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
264
268
|
const art = csn.definitions[newDefName];
|
|
265
269
|
if(art !== undefined) {
|
|
266
270
|
error(null, [ 'definitions', defName ], { name: newDefName },
|
|
267
|
-
|
|
271
|
+
'Artifact name containing dots can\'t be mapped to an OData compliant name because it conflicts with existing definition $(NAME)');
|
|
268
272
|
}
|
|
269
273
|
else {
|
|
270
274
|
csn.definitions[newDefName] = def;
|
|
@@ -275,36 +279,42 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
275
279
|
}
|
|
276
280
|
});
|
|
277
281
|
// rename type refs to new type names
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if(
|
|
282
|
-
|
|
282
|
+
const rewrite = (def) => {
|
|
283
|
+
const applyOnNode = (node) => {
|
|
284
|
+
node = node.items || node;
|
|
285
|
+
if(node.type && dotTypeNameMap[node.type]) {
|
|
286
|
+
node.type = dotTypeNameMap[node.type];
|
|
283
287
|
}
|
|
284
|
-
if(
|
|
285
|
-
|
|
288
|
+
if(node.target && dotEntityNameMap[node.target]) {
|
|
289
|
+
node.target = dotEntityNameMap[node.target];
|
|
286
290
|
}
|
|
287
|
-
if(
|
|
288
|
-
|
|
291
|
+
if(node.$path && dotEntityNameMap[node.$path[1]]) {
|
|
292
|
+
node.$path[1] = dotEntityNameMap[node.$path[1]]
|
|
289
293
|
}
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
// handle unbound action/function and params in views
|
|
293
|
-
_rewriteReferencesInActions(def);
|
|
294
|
-
});
|
|
294
|
+
rewriteReferencesInActions(node);
|
|
295
|
+
}
|
|
295
296
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
297
|
+
const rewriteReferencesInActions = (act) => {
|
|
298
|
+
act.params && Object.values(act.params).forEach(param => {
|
|
299
|
+
param = param.items || param;
|
|
300
|
+
if(param.type && (dotEntityNameMap[param.type] || dotTypeNameMap[param.type]))
|
|
301
|
+
param.type = dotEntityNameMap[param.type] || dotTypeNameMap[param.type];
|
|
302
|
+
});
|
|
303
|
+
if(act.returns){
|
|
304
|
+
const returnsObj = act.returns.items || act.returns;
|
|
305
|
+
if (returnsObj.type && dotEntityNameMap[returnsObj.type] || dotTypeNameMap[returnsObj.type])
|
|
306
|
+
returnsObj.type = dotEntityNameMap[returnsObj.type] || dotTypeNameMap[returnsObj.type];
|
|
307
|
+
}
|
|
306
308
|
}
|
|
307
|
-
|
|
309
|
+
|
|
310
|
+
forEachMemberRecursively(def, applyOnNode);
|
|
311
|
+
applyOnNode(def);
|
|
312
|
+
// handle unbound action/function and params in views
|
|
313
|
+
rewriteReferencesInActions(def);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
forEachDefinition(csn, rewrite);
|
|
317
|
+
forEachGeneric(csn, 'vocabularies', rewrite);
|
|
308
318
|
}
|
|
309
319
|
|
|
310
320
|
/*
|
|
@@ -366,7 +376,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
366
376
|
}
|
|
367
377
|
}
|
|
368
378
|
else {
|
|
369
|
-
error(null, subpath, { target: element.target },
|
|
379
|
+
error(null, subpath, { target: element.target }, 'Target $(TARGET) can\'t be found in the model');
|
|
370
380
|
}
|
|
371
381
|
}
|
|
372
382
|
// in V4 tag all compositions to be containments
|
|
@@ -892,14 +902,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
892
902
|
if(serviceRootNames.includes(targetSchemaName)) {
|
|
893
903
|
// remove all definitions starting with < fqSchemaName >. and add a schema reference
|
|
894
904
|
Object.keys(csn.definitions).forEach(dn => {
|
|
895
|
-
if(dn.startsWith(fqSchemaName)) {
|
|
905
|
+
if(dn.startsWith(fqSchemaName)) {
|
|
896
906
|
delete csn.definitions[dn];
|
|
897
907
|
delete reqDefs.definitions[dn];
|
|
898
908
|
}
|
|
899
909
|
});
|
|
900
910
|
if(!schemas[fqSchemaName])
|
|
901
911
|
schemaNames.push(fqSchemaName);
|
|
902
|
-
schemas[fqSchemaName] = createSchemaRef(targetSchemaName);
|
|
912
|
+
schemas[fqSchemaName] = edmUtils.createSchemaRef(serviceRoots, targetSchemaName);
|
|
903
913
|
}
|
|
904
914
|
}
|
|
905
915
|
});
|
|
@@ -1036,7 +1046,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1036
1046
|
function createSchemaRefFor(targetSchemaName) {
|
|
1037
1047
|
let ref = csn.definitions[globalSchemaPrefix + '.' + targetSchemaName];
|
|
1038
1048
|
if(!ref) {
|
|
1039
|
-
ref = createSchemaRef(targetSchemaName);
|
|
1049
|
+
ref = edmUtils.createSchemaRef(serviceRoots, targetSchemaName);
|
|
1040
1050
|
}
|
|
1041
1051
|
|
|
1042
1052
|
return ref;
|
|
@@ -1794,27 +1804,33 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1794
1804
|
}
|
|
1795
1805
|
}
|
|
1796
1806
|
|
|
1797
|
-
function
|
|
1807
|
+
function finalize(def, defName) {
|
|
1798
1808
|
// 1. let all doc props become @Core.Descriptions
|
|
1799
1809
|
// 2. mark a member that will become a collection
|
|
1800
1810
|
// 3. assign the edm primitive type to elements, to be used in the rendering later
|
|
1811
|
+
// 4. assign @Validation.AllowedValues to enums
|
|
1812
|
+
const path = ['definitions', defName];
|
|
1801
1813
|
edmUtils.assignAnnotation(def, '@Core.Description', def.doc);
|
|
1802
1814
|
markCollection(def);
|
|
1803
1815
|
mapCdsToEdmProp(def);
|
|
1816
|
+
annotateAllowedValues(def, path);
|
|
1804
1817
|
if (def.returns) {
|
|
1805
1818
|
markCollection(def.returns);
|
|
1806
1819
|
mapCdsToEdmProp(def.returns);
|
|
1820
|
+
annotateAllowedValues(def.returns, [...path, 'returns']);
|
|
1807
1821
|
}
|
|
1808
|
-
forEachMemberRecursively(def,member => {
|
|
1822
|
+
forEachMemberRecursively(def, (member, _memberName, _prop, path) => {
|
|
1809
1823
|
edmUtils.assignAnnotation(member, '@Core.Description', member.doc);
|
|
1810
1824
|
markCollection(member);
|
|
1811
1825
|
mapCdsToEdmProp(member);
|
|
1826
|
+
annotateAllowedValues(member, path);
|
|
1812
1827
|
ComputedDefaultValue(member);
|
|
1813
1828
|
if (member.returns) {
|
|
1814
1829
|
markCollection(member.returns);
|
|
1815
1830
|
mapCdsToEdmProp(member.returns);
|
|
1831
|
+
annotateAllowedValues(member.returns, [...path, 'returns']);
|
|
1816
1832
|
}
|
|
1817
|
-
});
|
|
1833
|
+
}, path);
|
|
1818
1834
|
// mark members that need to be rendered as collections
|
|
1819
1835
|
function markCollection(obj) {
|
|
1820
1836
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
@@ -1823,6 +1839,56 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1823
1839
|
edmUtils.assignProp(obj, '_isCollection', true);
|
|
1824
1840
|
}
|
|
1825
1841
|
}
|
|
1842
|
+
|
|
1843
|
+
/*
|
|
1844
|
+
Add @Validation.AllowedValues annotation for all enum types
|
|
1845
|
+
A 'Value' is added if the enum symbol:
|
|
1846
|
+
- has a valid value other than 'null'
|
|
1847
|
+
- has no value but the base type is cds.String, use the
|
|
1848
|
+
symbol as value
|
|
1849
|
+
*/
|
|
1850
|
+
function annotateAllowedValues(node, path) {
|
|
1851
|
+
let typeDef = node;
|
|
1852
|
+
if(!node.enum && node.type && !isBuiltinType(node.type))
|
|
1853
|
+
typeDef = csn.definitions[node.type];
|
|
1854
|
+
if(typeDef?.enum) {
|
|
1855
|
+
const enumValue = [];
|
|
1856
|
+
for(const enumSymbol in typeDef.enum) {
|
|
1857
|
+
let enumSymbolDef = typeDef.enum[enumSymbol];
|
|
1858
|
+
const result = { '@Core.SymbolicName': enumSymbol };
|
|
1859
|
+
if(enumSymbolDef['#'])
|
|
1860
|
+
enumSymbolDef = typeDef.enum[enumSymbolDef['#']];
|
|
1861
|
+
if(enumSymbolDef.val === undefined) {
|
|
1862
|
+
if(typeDef.type === 'cds.String') {
|
|
1863
|
+
// the symbol is used as value for type 'cds.String'
|
|
1864
|
+
result.Value = enumSymbol;
|
|
1865
|
+
enumValue.push(result);
|
|
1866
|
+
}
|
|
1867
|
+
else if(node.kind !== 'annotation')
|
|
1868
|
+
// omit the entry and warn
|
|
1869
|
+
warning('odata-enum-missing-value', path,
|
|
1870
|
+
{ name: enumSymbol, anno: '@Valiation.AllowedValues', type: typeDef.type },
|
|
1871
|
+
'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
|
|
1872
|
+
}
|
|
1873
|
+
else {
|
|
1874
|
+
// 'null' value is represented spec conform as empty record in AllowedValues collection
|
|
1875
|
+
//if(enumSymbolDef.val !== null)
|
|
1876
|
+
result.Value = enumSymbolDef.val;
|
|
1877
|
+
enumValue.push(result);
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// Can't rely that @description has already been renamed to @Core.Description
|
|
1881
|
+
// Eval description according to precedence (doc comment must be considered already in Odata transformer
|
|
1882
|
+
// as in contrast to the other doc commments as it is used to annotate the @Validation.AllowedValues)
|
|
1883
|
+
const desc = enumSymbolDef['@Core.Description'] || enumSymbolDef['@description'] || enumSymbolDef.doc;
|
|
1884
|
+
if (desc)
|
|
1885
|
+
result['@Core.Description'] = desc;
|
|
1886
|
+
}
|
|
1887
|
+
if(enumValue.length > 0)
|
|
1888
|
+
edmUtils.assignAnnotation(node, '@Validation.AllowedValues', enumValue);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1826
1892
|
}
|
|
1827
1893
|
|
|
1828
1894
|
// If containment in V4 is active, annotations that would be assigned to the containees
|
|
@@ -1830,8 +1896,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1830
1896
|
// the containment navigation property.
|
|
1831
1897
|
// Today only Capabilities.*Restrictions are known to be remapped as there exists a CDS
|
|
1832
1898
|
// short cut annotation @readonly that gets expanded and can be safely remapped.
|
|
1833
|
-
function
|
|
1899
|
+
function pullupCapabilitiesAnnotations(rootContainer) {
|
|
1834
1900
|
|
|
1901
|
+
if(!options.odataCapabilitiesPullup)
|
|
1902
|
+
return;
|
|
1835
1903
|
// meaningless for non-entities and proxies
|
|
1836
1904
|
if(rootContainer.kind !== 'entity' || rootContainer.$proxy)
|
|
1837
1905
|
return;
|
|
@@ -1867,7 +1935,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1867
1935
|
npe.NavigationProperty['='] &&
|
|
1868
1936
|
typeof npe.NavigationProperty['='] === 'string') {
|
|
1869
1937
|
applyTransformations({ definitions: { npe }}, {
|
|
1870
|
-
|
|
1938
|
+
'=': (parent, prop, value) => {
|
|
1871
1939
|
parent[prop] = prefix.concat(value).join('.');
|
|
1872
1940
|
}
|
|
1873
1941
|
});
|
|
@@ -1975,45 +2043,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1975
2043
|
// Helper section starts here
|
|
1976
2044
|
//
|
|
1977
2045
|
|
|
1978
|
-
|
|
1979
|
-
// create Cross Schema Reference object
|
|
1980
|
-
//
|
|
1981
|
-
function createSchemaRef(targetSchemaName) {
|
|
1982
|
-
// prepend as many path ups '..' as there are path steps in the service ref
|
|
1983
|
-
let serviceRef = path4(serviceRoots[targetSchemaName]).split('/').filter(c=>c.length);
|
|
1984
|
-
serviceRef.splice(0, 0, ...Array(serviceRef.length).fill('..'));
|
|
1985
|
-
// uncomment this to make $metadata absolute
|
|
1986
|
-
// if(serviceRef.length===0)
|
|
1987
|
-
// serviceRef.push('');
|
|
1988
|
-
if(serviceRef[serviceRef.length-1] !== '$metadata')
|
|
1989
|
-
serviceRef.push('$metadata');
|
|
1990
|
-
let sc = { kind: 'reference',
|
|
1991
|
-
name: targetSchemaName,
|
|
1992
|
-
ref: { Uri: serviceRef.join('/') },
|
|
1993
|
-
inc: { Namespace: targetSchemaName }
|
|
1994
|
-
};
|
|
1995
|
-
setProp(sc, '$mySchemaName', targetSchemaName);
|
|
1996
|
-
return sc;
|
|
1997
|
-
|
|
1998
|
-
/**
|
|
1999
|
-
* Resolve a service endpoint path to mount it to as follows...
|
|
2000
|
-
* Use _path or def[@path] if given (and remove leading '/')
|
|
2001
|
-
* Otherwise, use the service definition name with stripped 'Service'
|
|
2002
|
-
*/
|
|
2003
|
-
function path4 (def, _path = def['@path']) {
|
|
2004
|
-
if (_path)
|
|
2005
|
-
return _path.replace(/^\//, "");
|
|
2006
|
-
else
|
|
2007
|
-
return ( // generate one from the service's name
|
|
2008
|
-
/[^.]+$/.exec(def.name)[0] //> my.very.CatalogService --> CatalogService
|
|
2009
|
-
.replace(/Service$/,'') //> CatalogService --> Catalog
|
|
2010
|
-
.replace(/([a-z0-9])([A-Z])/g, (_,c,C) => c+'-'+C.toLowerCase()) //> ODataFooBarX9 --> odata-foo-bar-x9
|
|
2011
|
-
.replace(/_/g,'-') //> foo_bar_baz --> foo-bar-baz
|
|
2012
|
-
.toLowerCase() //> FOO --> foo
|
|
2013
|
-
)
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2046
|
+
|
|
2017
2047
|
function mapCdsToEdmProp(obj) {
|
|
2018
2048
|
if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
|
|
2019
2049
|
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const { setProp } = require('../base/model');
|
|
3
3
|
const { isBuiltinType, isEdmPropertyRendered, applyTransformations, cloneAnnotationValue } = require('../model/csnUtils');
|
|
4
|
-
const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require(
|
|
4
|
+
const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
|
|
5
5
|
|
|
6
6
|
/* eslint max-statements-per-line:off */
|
|
7
7
|
function validateOptions(_options)
|
|
@@ -205,7 +205,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
|
|
|
205
205
|
function fillConstraints(arg, pos)
|
|
206
206
|
{
|
|
207
207
|
if(arg.xpr)
|
|
208
|
-
arg.xpr
|
|
208
|
+
getExpressionArguments(arg.xpr);
|
|
209
209
|
else if(pos > 0 && pos < expr.length)
|
|
210
210
|
{
|
|
211
211
|
let lhs = expr[pos-1];
|
|
@@ -485,7 +485,7 @@ function mapCdsToEdmType(csn, messageFunctions, isV2=false, isMediaType=false, l
|
|
|
485
485
|
const { error } = messageFunctions || { error: ()=>true };
|
|
486
486
|
let cdsType = csn.type;
|
|
487
487
|
if(cdsType === undefined) {
|
|
488
|
-
error(null, location,
|
|
488
|
+
error(null, location, 'no type found');
|
|
489
489
|
return '<NOTYPE>';
|
|
490
490
|
}
|
|
491
491
|
if(!isBuiltinType(cdsType))
|
|
@@ -544,7 +544,7 @@ function mapCdsToEdmType(csn, messageFunctions, isV2=false, isMediaType=false, l
|
|
|
544
544
|
*/
|
|
545
545
|
}[cdsType];
|
|
546
546
|
if (edmType == undefined) {
|
|
547
|
-
error(null, location, { type: cdsType },
|
|
547
|
+
error(null, location, { type: cdsType }, 'No EDM type available for $(TYPE)');
|
|
548
548
|
}
|
|
549
549
|
if(isV2)
|
|
550
550
|
{
|
|
@@ -769,7 +769,7 @@ function mergeIntoNavPropEntry(annoPrefix, navPropEntry, prefix, props) {
|
|
|
769
769
|
|
|
770
770
|
// BEFORE merging found capabilities, prefix the paths
|
|
771
771
|
applyTransformations({ definitions: { o }}, {
|
|
772
|
-
|
|
772
|
+
'=': (parent, prop, value) => {
|
|
773
773
|
parent[prop] = prefix.concat(value).join('.');
|
|
774
774
|
}
|
|
775
775
|
});
|
|
@@ -830,11 +830,50 @@ function assignProp(obj, prop, value) {
|
|
|
830
830
|
}
|
|
831
831
|
}
|
|
832
832
|
|
|
833
|
+
//
|
|
834
|
+
// create Cross Schema Reference object
|
|
835
|
+
//
|
|
836
|
+
function createSchemaRef(serviceRoots, targetSchemaName) {
|
|
837
|
+
// prepend as many path ups '..' as there are path steps in the service ref
|
|
838
|
+
let serviceRef = path4(serviceRoots[targetSchemaName]).split('/').filter(c=>c.length);
|
|
839
|
+
serviceRef.splice(0, 0, ...Array(serviceRef.length).fill('..'));
|
|
840
|
+
// uncomment this to make $metadata absolute
|
|
841
|
+
// if(serviceRef.length===0)
|
|
842
|
+
// serviceRef.push('');
|
|
843
|
+
if(serviceRef[serviceRef.length-1] !== '$metadata')
|
|
844
|
+
serviceRef.push('$metadata');
|
|
845
|
+
let sc = { kind: 'reference',
|
|
846
|
+
name: targetSchemaName,
|
|
847
|
+
ref: { Uri: serviceRef.join('/') },
|
|
848
|
+
inc: { Namespace: targetSchemaName }
|
|
849
|
+
};
|
|
850
|
+
setProp(sc, '$mySchemaName', targetSchemaName);
|
|
851
|
+
return sc;
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Resolve a service endpoint path to mount it to as follows...
|
|
855
|
+
* Use _path or def[@path] if given (and remove leading '/')
|
|
856
|
+
* Otherwise, use the service definition name with stripped 'Service'
|
|
857
|
+
*/
|
|
858
|
+
function path4 (def, _path = def['@path']) {
|
|
859
|
+
if (_path)
|
|
860
|
+
return _path.replace(/^\//, '');
|
|
861
|
+
else
|
|
862
|
+
return ( // generate one from the service's name
|
|
863
|
+
/[^.]+$/.exec(def.name)[0] //> my.very.CatalogService --> CatalogService
|
|
864
|
+
.replace(/Service$/,'') //> CatalogService --> Catalog
|
|
865
|
+
.replace(/([a-z0-9])([A-Z])/g, (_,c,C) => c+'-'+C.toLowerCase()) //> ODataFooBarX9 --> odata-foo-bar-x9
|
|
866
|
+
.replace(/_/g,'-') //> foo_bar_baz --> foo-bar-baz
|
|
867
|
+
.toLowerCase() //> FOO --> foo
|
|
868
|
+
)
|
|
869
|
+
}
|
|
870
|
+
}
|
|
833
871
|
|
|
834
872
|
|
|
835
873
|
module.exports = {
|
|
836
874
|
assignAnnotation,
|
|
837
875
|
assignProp,
|
|
876
|
+
createSchemaRef,
|
|
838
877
|
validateOptions,
|
|
839
878
|
intersect,
|
|
840
879
|
foreach,
|