@sap/cds-compiler 3.4.4 → 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 +72 -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 +16 -6
- 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
|
@@ -18,7 +18,7 @@ const booleanBuiltin = 'cds.Boolean';
|
|
|
18
18
|
* @param {string} pathDelimiter
|
|
19
19
|
* @param {object} messageFunctions
|
|
20
20
|
*/
|
|
21
|
-
function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
21
|
+
function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
22
22
|
const draftSuffix = '.drafts';
|
|
23
23
|
// All services of the model - needed for drafts
|
|
24
24
|
const allServices = getServiceNames(csn);
|
|
@@ -38,7 +38,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
38
38
|
* @param {CSN.Artifact} artifact
|
|
39
39
|
* @param {string} artifactName
|
|
40
40
|
*/
|
|
41
|
-
function generateDraft(artifact, artifactName) {
|
|
41
|
+
function generateDraft( artifact, artifactName ) {
|
|
42
42
|
if ((artifact.kind === 'entity') &&
|
|
43
43
|
hasAnnotationValue(artifact, draftAnnotation) &&
|
|
44
44
|
isPartOfService(artifactName)) {
|
|
@@ -69,7 +69,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
69
69
|
* @param {CSN.Artifact} rootArtifact root artifact where composition traversal started.
|
|
70
70
|
* @param {object} draftNodes Dictionary of artifacts
|
|
71
71
|
*/
|
|
72
|
-
function collectDraftNodesInto(artifact, artifactName, rootArtifact, draftNodes) {
|
|
72
|
+
function collectDraftNodesInto( artifact, artifactName, rootArtifact, draftNodes ) {
|
|
73
73
|
// Collect the artifact itself
|
|
74
74
|
draftNodes[artifactName] = artifact;
|
|
75
75
|
// Follow all composition targets in elements of 'artifact'
|
|
@@ -108,7 +108,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
108
108
|
* @param {string} artifactName
|
|
109
109
|
* @param {string} draftRootName
|
|
110
110
|
*/
|
|
111
|
-
function generateDraftForHana(artifact, artifactName, draftRootName) {
|
|
111
|
+
function generateDraftForHana( artifact, artifactName, draftRootName ) {
|
|
112
112
|
// Sanity check
|
|
113
113
|
if (!isPartOfService(artifactName))
|
|
114
114
|
throw new ModelError(`Expecting artifact to be part of a service: ${JSON.stringify(artifact)}`);
|
|
@@ -234,7 +234,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
234
234
|
* @param {CSN.Association} association Assoc to check
|
|
235
235
|
* @returns {object}
|
|
236
236
|
*/
|
|
237
|
-
function getDraftUUIDKey(association) {
|
|
237
|
+
function getDraftUUIDKey( association ) {
|
|
238
238
|
if (association.keys) {
|
|
239
239
|
const filtered = association.keys.filter(o => (o.ref && !o.as && o.ref.length === 1 && o.ref[0] === 'DraftUUID') || (o.as && o.as === 'DraftUUID'));
|
|
240
240
|
if (filtered.length === 1)
|
|
@@ -253,7 +253,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
253
253
|
* @param {object} obj Any object with at least "ref"
|
|
254
254
|
* @returns {string}
|
|
255
255
|
*/
|
|
256
|
-
function getNameForRef(obj) {
|
|
256
|
+
function getNameForRef( obj ) {
|
|
257
257
|
if (obj.as)
|
|
258
258
|
return obj.as;
|
|
259
259
|
|
|
@@ -285,7 +285,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
285
285
|
* @param {CSN.Artifact} artifact
|
|
286
286
|
* @param {CSN.Artifact[]} draftNodes
|
|
287
287
|
*/
|
|
288
|
-
function redirectDraftTargets(artifact, draftNodes) {
|
|
288
|
+
function redirectDraftTargets( artifact, draftNodes ) {
|
|
289
289
|
for (const elemName in artifact.elements) {
|
|
290
290
|
const elem = artifact.elements[elemName];
|
|
291
291
|
if (elem.target) {
|
|
@@ -310,7 +310,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
310
310
|
* @param {string} draftNodeName
|
|
311
311
|
* @returns {object} Object with shadowTarget: definition and shadowTargetName: Name of the definition
|
|
312
312
|
*/
|
|
313
|
-
function getDraftShadowEntityFor(draftNode, draftNodeName) {
|
|
313
|
+
function getDraftShadowEntityFor( draftNode, draftNodeName ) {
|
|
314
314
|
// Sanity check
|
|
315
315
|
if (!draftNodes[draftNodeName])
|
|
316
316
|
throw new ModelError(`Not a draft node: ${draftNodeName}`);
|
|
@@ -325,7 +325,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
325
325
|
* @param {string} artifactName Absolute name of the artifact
|
|
326
326
|
* @returns {boolean}
|
|
327
327
|
*/
|
|
328
|
-
function isPartOfService(artifactName) {
|
|
328
|
+
function isPartOfService( artifactName ) {
|
|
329
329
|
for (const serviceName of allServices) {
|
|
330
330
|
if (artifactName.startsWith(`${serviceName}.`))
|
|
331
331
|
return true;
|
|
@@ -340,7 +340,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
340
340
|
* @param {string} artifactName Absolute name of the artifact
|
|
341
341
|
* @returns {false|string} Name of the service or false if no match is found.
|
|
342
342
|
*/
|
|
343
|
-
function getMatchingService(artifactName) {
|
|
343
|
+
function getMatchingService( artifactName ) {
|
|
344
344
|
const matches = [];
|
|
345
345
|
for (const serviceName of allServices) {
|
|
346
346
|
if (artifactName.startsWith(`${serviceName}.`))
|
|
@@ -23,7 +23,7 @@ const { makeMessageFunction } = require('../../base/messages');
|
|
|
23
23
|
* @todo should be done by the compiler - Check associations for valid foreign keys
|
|
24
24
|
* @todo check if needed at all: Remove '$projection' from paths in the element's ON-condition
|
|
25
25
|
*/
|
|
26
|
-
function generateDrafts(csn, options, services) {
|
|
26
|
+
function generateDrafts( csn, options, services ) {
|
|
27
27
|
const {
|
|
28
28
|
createForeignKeyElement,
|
|
29
29
|
createAndAddDraftAdminDataProjection, createScalarElement,
|
|
@@ -73,7 +73,7 @@ function generateDrafts(csn, options, services) {
|
|
|
73
73
|
* @param {string} artifactName
|
|
74
74
|
* @param {CSN.Artifact} rootArtifact artifact where composition traversal started
|
|
75
75
|
*/
|
|
76
|
-
function generateDraftForOdata(artifact, artifactName, rootArtifact) {
|
|
76
|
+
function generateDraftForOdata( artifact, artifactName, rootArtifact ) {
|
|
77
77
|
// Sanity check
|
|
78
78
|
// @ts-ignore
|
|
79
79
|
if (!isArtifactInSomeService(artifactName, services))
|
|
@@ -10,8 +10,8 @@ const { cloneCsnNonDict,
|
|
|
10
10
|
getArtifactDatabaseNameOf,
|
|
11
11
|
getElementDatabaseNameOf,
|
|
12
12
|
isAspect,
|
|
13
|
-
isBuiltinType,
|
|
14
13
|
getServiceNames,
|
|
14
|
+
forEachGeneric,
|
|
15
15
|
} = require('../model/csnUtils');
|
|
16
16
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
17
17
|
const validate = require('../checks/validator');
|
|
@@ -165,11 +165,11 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
165
165
|
// Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
|
|
166
166
|
// OData doesn't resolve type chains after the first 'items'
|
|
167
167
|
flattening.resolveTypeReferences(csn, options, resolved, '_',
|
|
168
|
-
{ skip: [ 'action', 'aspect', 'event', 'function', 'type'],
|
|
168
|
+
{ skip: [ 'action', 'aspect', 'event', 'function', 'type'],
|
|
169
169
|
skipArtifact: isExternalServiceMember, skipStandard: { items: true } });
|
|
170
170
|
// No structured elements exists anymore
|
|
171
171
|
flattening.flattenElements(csn, options, '_', error,
|
|
172
|
-
{ skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
172
|
+
{ skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
173
173
|
skipArtifact: isExternalServiceMember,
|
|
174
174
|
skipStandard: { items: true }, // don't drill further into .items
|
|
175
175
|
skipDict: { actions: true } }); // don't drill further into .actions -> bound actions, action-artifacts are handled by skip
|
|
@@ -213,14 +213,14 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
213
213
|
const skipPersNameKinds = {'service':1, 'context':1, 'namespace':1, 'annotation':1, 'action':1, 'function':1};
|
|
214
214
|
forEachDefinition(csn, (def, defName) => {
|
|
215
215
|
// Resolve annotation shorthands for entities, types, annotations, ...
|
|
216
|
-
renameShorthandAnnotations(def
|
|
216
|
+
renameShorthandAnnotations(def);
|
|
217
217
|
|
|
218
218
|
// Annotate artifacts with their DB names if requested.
|
|
219
219
|
// Skip artifacts that have no DB equivalent anyway
|
|
220
220
|
if (options.sqlMapping && !(def.kind in skipPersNameKinds))
|
|
221
221
|
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
|
|
222
222
|
|
|
223
|
-
forEachMemberRecursively(def, (member, memberName, propertyName
|
|
223
|
+
forEachMemberRecursively(def, (member, memberName, propertyName) => {
|
|
224
224
|
// Annotate elements, foreign keys, parameters, etc. with their DB names if requested
|
|
225
225
|
// Only these are actually required and don't annotate virtual elements in entities or types
|
|
226
226
|
// as they have no DB representation (although in views)
|
|
@@ -233,7 +233,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
233
233
|
annotateCoreComputed(member);
|
|
234
234
|
|
|
235
235
|
// Resolve annotation shorthands for elements, actions, action parameters
|
|
236
|
-
renameShorthandAnnotations(member
|
|
236
|
+
renameShorthandAnnotations(member);
|
|
237
237
|
|
|
238
238
|
// - If the association target is annotated with @cds.odata.valuelist, annotate the
|
|
239
239
|
// association with @Common.ValueList.viaAssociation
|
|
@@ -255,11 +255,15 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
255
255
|
}
|
|
256
256
|
}, { skipArtifact: isExternalServiceMember })
|
|
257
257
|
|
|
258
|
+
if(isBetaEnabled(options, 'odataTerms')) {
|
|
259
|
+
forEachGeneric(csn, 'vocabularies', renameShorthandAnnotations);
|
|
260
|
+
}
|
|
261
|
+
|
|
258
262
|
// Throw exception in case of errors
|
|
259
263
|
throwWithAnyError();
|
|
260
264
|
cleanup();
|
|
261
265
|
if (options.testMode) csn = cloneCsnNonDict(csn, options); // sort, keep hidden properties
|
|
262
|
-
timetrace.stop();
|
|
266
|
+
timetrace.stop('OData transformation');
|
|
263
267
|
return csn;
|
|
264
268
|
|
|
265
269
|
// TODO: Move this to checks?
|
|
@@ -298,7 +302,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
298
302
|
|
|
299
303
|
// Rename shorthand annotations within artifact or element 'node' according to a builtin
|
|
300
304
|
// list.
|
|
301
|
-
function renameShorthandAnnotations(node
|
|
305
|
+
function renameShorthandAnnotations(node) {
|
|
302
306
|
// FIXME: Verify this list - are they all still required? Do we need any more?
|
|
303
307
|
const mappings = {
|
|
304
308
|
'@label': '@Common.Label',
|
|
@@ -362,38 +366,6 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
362
366
|
setAnnotation(node, '@Validation.Minimum', node['@assert.range'][0]);
|
|
363
367
|
setAnnotation(node, '@Validation.Maximum', node['@assert.range'][1]);
|
|
364
368
|
}
|
|
365
|
-
// for enums @assert.range changes into a boolean annotation
|
|
366
|
-
else if (node[name] === true) {
|
|
367
|
-
let typeDef = node;
|
|
368
|
-
if(!node.enum && node.type && !isBuiltinType(node.type))
|
|
369
|
-
typeDef = csn.definitions[node.type];
|
|
370
|
-
if(typeDef.enum) {
|
|
371
|
-
const enumValue = [];
|
|
372
|
-
for(const enumSymbol in typeDef.enum) {
|
|
373
|
-
const enumSymbolDef = typeDef.enum[enumSymbol];
|
|
374
|
-
if(enumSymbolDef.val === null)
|
|
375
|
-
info('odata-enum-value-type', path,
|
|
376
|
-
{name: enumSymbol, value: null, anno: '@Valiation.AllowedValues' },
|
|
377
|
-
'Value $(VALUE) for enum element $(NAME) not added to $(ANNO)');
|
|
378
|
-
else {
|
|
379
|
-
const result = { '@Core.SymbolicName': enumSymbol };
|
|
380
|
-
if (enumSymbolDef.val !== undefined)
|
|
381
|
-
result.Value = enumSymbolDef.val;
|
|
382
|
-
else if (node.type && node.type === 'cds.String')
|
|
383
|
-
// the symbol is used as value only for type 'cds.String'
|
|
384
|
-
result.Value = enumSymbol;
|
|
385
|
-
// Can't rely that @description has already been renamed to @Core.Description
|
|
386
|
-
// Eval description according to precedence (doc comment must be considered already in Odata transformer
|
|
387
|
-
// as in contrast to the other doc commments as it is used to annotate the @Validation.AllowedValues)
|
|
388
|
-
const desc = enumSymbolDef['@Core.Description'] || enumSymbolDef['@description'] || enumSymbolDef.doc;
|
|
389
|
-
if (desc)
|
|
390
|
-
result['@Core.Description'] = desc;
|
|
391
|
-
enumValue.push(result);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
setAnnotation(node, '@Validation.AllowedValues', enumValue);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
369
|
}
|
|
398
370
|
});
|
|
399
371
|
}
|
|
@@ -4,7 +4,7 @@ const { setProp, isBetaEnabled } = require('../base/model');
|
|
|
4
4
|
const { getUtils, cloneCsnNonDict,
|
|
5
5
|
forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
|
|
6
6
|
getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
|
|
7
|
-
isAspect, walkCsnPath,
|
|
7
|
+
isAspect, walkCsnPath, isPersistedOnDatabase,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const { makeMessageFunction } = require('../base/messages');
|
|
10
10
|
const transformUtils = require('./transformUtilsNew');
|
|
@@ -29,7 +29,7 @@ const { getViewTransformer } = require('./db/views');
|
|
|
29
29
|
const cdsPersistence = require('./db/cdsPersistence');
|
|
30
30
|
const temporal = require('./db/temporal');
|
|
31
31
|
const associations = require('./db/associations')
|
|
32
|
-
const { ModelError } = require(
|
|
32
|
+
const { ModelError } = require('../base/error');
|
|
33
33
|
|
|
34
34
|
// By default: Do not process non-entities/views
|
|
35
35
|
function forEachDefinition(csn, cb) {
|
|
@@ -103,8 +103,11 @@ function forEachDefinition(csn, cb) {
|
|
|
103
103
|
function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
104
104
|
// copy the model as we don't want to change the input model
|
|
105
105
|
timetrace.start('HANA transformation');
|
|
106
|
+
|
|
107
|
+
timetrace.start('Clone CSN');
|
|
106
108
|
/** @type {CSN.Model} */
|
|
107
109
|
let csn = cloneCsnNonDict(inputModel, options);
|
|
110
|
+
timetrace.stop('Clone CSN');
|
|
108
111
|
|
|
109
112
|
checkCSNVersion(csn, options);
|
|
110
113
|
|
|
@@ -130,10 +133,12 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
130
133
|
if (!doA2J)
|
|
131
134
|
forEachDefinition(csn, handleMixinOnConditions);
|
|
132
135
|
|
|
136
|
+
timetrace.start('Validate');
|
|
133
137
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
134
138
|
const cleanup = validate.forRelationalDB(csn, {
|
|
135
139
|
message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, isAspect
|
|
136
140
|
});
|
|
141
|
+
timetrace.stop('Validate');
|
|
137
142
|
|
|
138
143
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
139
144
|
handleExists(csn, options, error);
|
|
@@ -153,9 +158,11 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
153
158
|
const transformCsn = transformUtils.transformModel;
|
|
154
159
|
|
|
155
160
|
|
|
161
|
+
timetrace.start('temporal');
|
|
156
162
|
// (001) Add a temporal where condition to views where applicable before assoc2join
|
|
157
163
|
// assoc2join eventually rewrites the table aliases
|
|
158
164
|
forEachDefinition(csn, temporal.getViewDecorator(csn, {info}));
|
|
165
|
+
timetrace.stop('temporal');
|
|
159
166
|
|
|
160
167
|
// check unique constraints - further processing is done in rewriteUniqueConstraints
|
|
161
168
|
assertUnique.prepare(csn, options, error, info);
|
|
@@ -227,6 +234,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
227
234
|
cloneWithTransformations,
|
|
228
235
|
} = csnUtils;
|
|
229
236
|
|
|
237
|
+
timetrace.start('Transform CSN')
|
|
238
|
+
|
|
230
239
|
// (000) Rename primitive types, make UUID a String
|
|
231
240
|
transformCsn(csn, {
|
|
232
241
|
type: (val, node, key) => {
|
|
@@ -278,9 +287,9 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
278
287
|
addLocalizationViews(csn, options);
|
|
279
288
|
|
|
280
289
|
!doA2J && forEachDefinition(csn, (definition, artName, prop, path) => {
|
|
281
|
-
if (definition.query) {
|
|
290
|
+
if (definition.query && isPersistedOnDatabase(definition)) {
|
|
282
291
|
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
283
|
-
const that = { csnUtils
|
|
292
|
+
const that = { csnUtils, options, error };
|
|
284
293
|
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path)
|
|
285
294
|
}
|
|
286
295
|
});
|
|
@@ -351,8 +360,6 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
351
360
|
|
|
352
361
|
throwWithAnyError();
|
|
353
362
|
|
|
354
|
-
timetrace.stop();
|
|
355
|
-
|
|
356
363
|
function killProp(parent, prop){
|
|
357
364
|
delete parent[prop];
|
|
358
365
|
}
|
|
@@ -408,6 +415,9 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
408
415
|
|
|
409
416
|
redoProjections.forEach(fn => fn());
|
|
410
417
|
|
|
418
|
+
timetrace.stop('Transform CSN');
|
|
419
|
+
timetrace.stop('HANA transformation');
|
|
420
|
+
|
|
411
421
|
return csn;
|
|
412
422
|
|
|
413
423
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
@@ -724,8 +724,8 @@ function checkExistingLocalizationViews(csn, options, messageFunctions) {
|
|
|
724
724
|
if (!def.query && !def.projection) {
|
|
725
725
|
if (!name.endsWith('.texts')) {
|
|
726
726
|
hasNonViews = true;
|
|
727
|
-
messageFunctions.error('reserved-namespace-localized', ['definitions', name], {},
|
|
728
|
-
'The namespace
|
|
727
|
+
messageFunctions.error('reserved-namespace-localized', ['definitions', name], { name: 'localized' },
|
|
728
|
+
'The namespace $(NAME) is reserved for localization views');
|
|
729
729
|
}
|
|
730
730
|
} else if (!hasExistingViews) {
|
|
731
731
|
hasExistingViews = true;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { isBetaEnabled } = require('../../base/model');
|
|
3
4
|
const {
|
|
4
|
-
forEachDefinition, forEachMemberRecursively,
|
|
5
|
+
forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
5
6
|
isBuiltinType, cloneCsnDictionary, cloneCsnNonDict,
|
|
6
7
|
} = require('../../model/csnUtils');
|
|
7
8
|
const { isArtifactInSomeService, isArtifactInService } = require('./utils');
|
|
@@ -22,32 +23,33 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
22
23
|
expandToFinalBaseType(def.items, defName);
|
|
23
24
|
expandToFinalBaseType(def.returns, defName);
|
|
24
25
|
expandToFinalBaseType(def.returns && def.returns.items, defName);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
26
|
+
/*
|
|
27
|
+
If the definition('def' variable) is a type definition and the assigned type of this very same definition('def' variable)
|
|
28
|
+
is structured type, e.g.:
|
|
29
|
+
|
|
30
|
+
type Struct1 {
|
|
31
|
+
a : Integer;
|
|
32
|
+
b : Integer;
|
|
33
|
+
};
|
|
34
|
+
type Struct2: Struct1;
|
|
35
|
+
after compilation the csn looks like this:
|
|
36
|
+
...
|
|
37
|
+
"S.Struct1": {
|
|
38
|
+
"kind": "type",
|
|
39
|
+
"elements": {
|
|
40
|
+
"a": { "type": "cds.Integer" },
|
|
41
|
+
"b": { "type": "cds.Integer" }
|
|
42
|
+
} },
|
|
43
|
+
"S.Struct2": {
|
|
44
|
+
"kind": "type",
|
|
45
|
+
"type": "S.Struct1",
|
|
46
|
+
"elements": {
|
|
47
|
+
"a": { "type": "cds.Integer" },
|
|
48
|
+
"b": { "type": "cds.Integer" }
|
|
49
|
+
} } ...
|
|
50
|
+
|
|
51
|
+
"S.Struct2" should looks just like "S.Struct1" => the "type": "S.Struct1" property has to be removed
|
|
52
|
+
*/
|
|
51
53
|
if (def.kind === 'type' && def.type && !isBuiltinType(def.type) && !def.type.ref) {
|
|
52
54
|
// elements are already there -> do not show the type
|
|
53
55
|
delete def.type;
|
|
@@ -62,6 +64,18 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
62
64
|
}
|
|
63
65
|
}, { skipArtifact: isExternalServiceMember });
|
|
64
66
|
|
|
67
|
+
if(isBetaEnabled(options, 'odataTerms')) {
|
|
68
|
+
forEachGeneric(csn, 'vocabularies', (def, defName) => {
|
|
69
|
+
forEachMemberRecursively(def, (member) => {
|
|
70
|
+
expandToFinalBaseType(member, defName);
|
|
71
|
+
expandToFinalBaseType(member.items, defName);
|
|
72
|
+
|
|
73
|
+
}, ['vocabularies', defName]);
|
|
74
|
+
|
|
75
|
+
expandToFinalBaseType(def, defName);
|
|
76
|
+
expandToFinalBaseType(def.items, defName);
|
|
77
|
+
}, [], { skipArtifact: isExternalServiceMember });
|
|
78
|
+
}
|
|
65
79
|
// In case we have in the model something like:
|
|
66
80
|
// type Foo: array of Bar; type Bar: { qux: Integer };
|
|
67
81
|
// In the type Foo we expand the first level of elements of the items like we have in CDL this:
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const { setProp } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
|
|
11
|
+
const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember, forEachGeneric } = require('../../model/csnUtils');
|
|
12
12
|
const { copyAnnotations } = require('../../model/csnUtils');
|
|
13
13
|
const { isBetaEnabled } = require('../../base/model.js');
|
|
14
14
|
|
|
@@ -49,6 +49,31 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
if(isBetaEnabled(options, 'odataTerms')) {
|
|
53
|
+
forEachGeneric(csn, 'vocabularies', (def, defName, _propertyName, path) => {
|
|
54
|
+
// we do expose types only for definition from inside services
|
|
55
|
+
const serviceName = whatsMyServiceName(defName, false);
|
|
56
|
+
// run type exposure only on requested services if not in multi schema mode
|
|
57
|
+
// multi schema mode requires a proper type exposure for all services as a prerequisite
|
|
58
|
+
// for the proxy exposure
|
|
59
|
+
if (serviceName && requestedServiceNames.includes(serviceName)) {
|
|
60
|
+
if(csn.definitions[defName]) {
|
|
61
|
+
// error, duplicate definitions not allowed!
|
|
62
|
+
// TODO: Use path as error location as soon as refs outside definitions are supported
|
|
63
|
+
error('odata-definition-exists', ['vocabularies', defName], { anno: defName, '#':'anno' });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// link def into definitions for later use
|
|
67
|
+
def.kind = 'annotation';
|
|
68
|
+
csn.definitions[defName] = def;
|
|
69
|
+
const artificialName = `term_${defName.replace(/\./g, '_')}`;//_${paramName}`;
|
|
70
|
+
const newTypeName = getNewTypeName(undefined, undefined, artificialName, serviceName);
|
|
71
|
+
exposeTypeOf(def, false, defName, defName, serviceName, newTypeName, path.concat(['vocabularies', defName]), undefined, true);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
52
77
|
return schemas;
|
|
53
78
|
|
|
54
79
|
/**
|
|
@@ -82,14 +107,14 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
82
107
|
* @param {String} serviceName
|
|
83
108
|
* @param {String} newTypeName
|
|
84
109
|
*/
|
|
85
|
-
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName) {
|
|
86
|
-
const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable(
|
|
110
|
+
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName, isTermDef=false) {
|
|
111
|
+
const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable();
|
|
87
112
|
if (isExposable) {
|
|
88
113
|
// this is the name used to register the new type in csn.definitions
|
|
89
114
|
let fullQualifiedNewTypeName =
|
|
90
115
|
isMultiSchema
|
|
91
|
-
? (node.type || (node.items
|
|
92
|
-
? getTypeNameInMultiSchema(node.type|| (node.items
|
|
116
|
+
? (node.type || (node.items?.type)
|
|
117
|
+
? getTypeNameInMultiSchema(node.type|| (node.items?.type), serviceName)
|
|
93
118
|
: getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
|
|
94
119
|
: `${serviceName}.${newTypeName}`;
|
|
95
120
|
|
|
@@ -100,7 +125,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
100
125
|
isKey = false;
|
|
101
126
|
// in case this was a named type and if the openess does not match the type definition
|
|
102
127
|
// expose the type as a new one not changing the original definition.
|
|
103
|
-
if((!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
|
|
128
|
+
if(elements && (!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
|
|
104
129
|
fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
|
|
105
130
|
}
|
|
106
131
|
// check if that type is already defined
|
|
@@ -118,51 +143,62 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
118
143
|
/* Expose new structured type
|
|
119
144
|
* Treat items.elements as ordinary elements for now.
|
|
120
145
|
*/
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
146
|
+
if(elements) {
|
|
147
|
+
newType = createNewStructType(elements);
|
|
148
|
+
// if using node enforces open/closed, set it on type
|
|
149
|
+
if(node['@open'] !== undefined)
|
|
150
|
+
newType['@open'] = node['@open']
|
|
151
|
+
if (node.$location)
|
|
152
|
+
setProp(newType, '$location', node.$location);
|
|
127
153
|
|
|
128
|
-
|
|
129
|
-
|
|
154
|
+
csn.definitions[fullQualifiedNewTypeName] = newType;
|
|
155
|
+
exposedTypes[fullQualifiedNewTypeName] = 1;
|
|
130
156
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
// Recurse into elements of 'type' (if any) and expose them as well (is needed)
|
|
158
|
+
newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
|
|
159
|
+
if (node.elements && node.elements[elemName].$location)
|
|
160
|
+
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
161
|
+
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
162
|
+
{
|
|
163
|
+
const { isExposable, typeDef, typeName } = exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
164
|
+
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName, isTermDef);
|
|
165
|
+
// if the type for the newElem was not exposed it may be a scalar type def from an external service that hasn't
|
|
166
|
+
// been catched by expandToFinalBaseType() (forODataNew must not modify external imported services)
|
|
167
|
+
if(!isExposable && isBuiltinType(typeName) && !isBuiltinType((newElem.items?.type || newElem.type))) {
|
|
168
|
+
if(typeDef.items) {
|
|
169
|
+
newElem.items = typeDef.items;
|
|
170
|
+
delete newElem.type;
|
|
171
|
+
}
|
|
172
|
+
else if(newElem.items) {
|
|
173
|
+
newElem.items.type = typeName;
|
|
174
|
+
if(typeDef.enum)
|
|
175
|
+
newElem.items.enum = typeDef.enum;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
newElem.type = typeName;
|
|
179
|
+
if(typeDef.enum)
|
|
180
|
+
newElem.enum = typeDef.enum;
|
|
181
|
+
}
|
|
155
182
|
}
|
|
156
183
|
}
|
|
184
|
+
});
|
|
185
|
+
copyAnnotations(typeDef, newType);
|
|
186
|
+
// if the origin type had items, add items to exposed type
|
|
187
|
+
if(typeDef.kind === 'type') {
|
|
188
|
+
if(typeDef.items) {
|
|
189
|
+
newType.items = { elements: newType.elements };
|
|
190
|
+
delete newType.elements;
|
|
191
|
+
}
|
|
157
192
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
newType.items = { elements: newType.elements };
|
|
164
|
-
delete newType.elements;
|
|
193
|
+
}
|
|
194
|
+
else if(isTermDef) {
|
|
195
|
+
newType = Object.create(null);
|
|
196
|
+
for(let n in typeDef) {
|
|
197
|
+
newType[n] = typeDef[n];
|
|
165
198
|
}
|
|
199
|
+
newType.kind = 'type';
|
|
200
|
+
csn.definitions[fullQualifiedNewTypeName] = newType;
|
|
201
|
+
exposedTypes[fullQualifiedNewTypeName] = 1;
|
|
166
202
|
}
|
|
167
203
|
}
|
|
168
204
|
// adjust current node to new type
|
|
@@ -188,36 +224,44 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
188
224
|
* - the elements dictionary that needs to be cloned
|
|
189
225
|
* - the typeDef, either the resolved type def or the node itself
|
|
190
226
|
* - if structured type was anonymously defined
|
|
191
|
-
* @param {object} node
|
|
192
227
|
* @returns {object} { isExposable, typeDef, typeName, elements, isAnonymous }
|
|
193
228
|
*/
|
|
194
|
-
function isTypeExposable(
|
|
229
|
+
function isTypeExposable() {
|
|
195
230
|
let typeName = undefined;
|
|
196
231
|
let typeDef = node;
|
|
197
|
-
let elements = (node.items
|
|
232
|
+
let elements = (node.items?.elements || node.elements)
|
|
198
233
|
// anonymous structured type
|
|
199
234
|
if(elements)
|
|
200
235
|
return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
|
|
201
236
|
// named type, resolve the type to inspect it
|
|
202
|
-
let type = node.items
|
|
237
|
+
let type = node.items?.type || node.type;
|
|
203
238
|
if(type) {
|
|
204
239
|
typeName = (type.ref && csnUtils.getFinalType(type)) || type;
|
|
205
|
-
typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
206
240
|
const rc = { isExposable: true, typeDef, typeName, isAnonymous: false };
|
|
207
|
-
if(!isBuiltinType(typeName)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
if(!isBuiltinType(typeName)) {
|
|
242
|
+
rc.typeDef = typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
243
|
+
if(!isArtifactInService(typeName, serviceName)) {
|
|
244
|
+
while(!isBuiltinType(typeName)) {
|
|
245
|
+
typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
246
|
+
if(typeDef) {
|
|
247
|
+
if((isTermDef && typeDef.enum) || (rc.elements = (typeDef.items?.elements || typeDef.elements)) !== undefined)
|
|
248
|
+
return rc;
|
|
249
|
+
type = typeDef.items?.type || typeDef.type;
|
|
250
|
+
typeName = (type.ref && csnUtils.getFinalType(type)) || type;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
throw Error(`Please debug me: ${typeName} not found`);
|
|
254
|
+
}
|
|
218
255
|
}
|
|
219
256
|
}
|
|
257
|
+
else {
|
|
258
|
+
rc.isExposable = false;
|
|
259
|
+
return rc;
|
|
260
|
+
}
|
|
220
261
|
}
|
|
262
|
+
// else if(isTermDef && typeDef.enum) {
|
|
263
|
+
// return rc;
|
|
264
|
+
// }
|
|
221
265
|
}
|
|
222
266
|
return { isExposable: false, typeDef, typeName, isAnonymous: false };
|
|
223
267
|
}
|
|
@@ -308,7 +352,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
308
352
|
*/
|
|
309
353
|
function getNewTypeName(element, elementName, typeNamePrefix, serviceName) {
|
|
310
354
|
// for the new type name node.type has precedence over node.items.type
|
|
311
|
-
const typeName = (!element
|
|
355
|
+
const typeName = (!element?.elements && element?.type || !element?.items?.elements && element?.items?.type);
|
|
312
356
|
return typeName
|
|
313
357
|
? `${isMultiSchema
|
|
314
358
|
? typeName.split('.').pop() // use leaf element
|