@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +12 -12
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +9 -1
  7. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  8. package/lib/api/main.js +58 -59
  9. package/lib/api/options.js +4 -2
  10. package/lib/api/validate.js +2 -2
  11. package/lib/base/cleanSymbols.js +2 -3
  12. package/lib/base/dictionaries.js +6 -6
  13. package/lib/base/error.js +2 -2
  14. package/lib/base/keywords.js +6 -6
  15. package/lib/base/location.js +11 -12
  16. package/lib/base/message-registry.js +124 -28
  17. package/lib/base/messages.js +247 -179
  18. package/lib/base/model.js +14 -11
  19. package/lib/base/node-helpers.js +9 -10
  20. package/lib/base/optionProcessorHelper.js +138 -129
  21. package/lib/checks/actionsFunctions.js +5 -5
  22. package/lib/checks/annotationsOData.js +4 -4
  23. package/lib/checks/arrayOfs.js +1 -1
  24. package/lib/checks/cdsPersistence.js +1 -1
  25. package/lib/checks/checkForTypes.js +3 -3
  26. package/lib/checks/defaultValues.js +3 -3
  27. package/lib/checks/elements.js +7 -7
  28. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  29. package/lib/checks/foreignKeys.js +1 -1
  30. package/lib/checks/invalidTarget.js +4 -4
  31. package/lib/checks/managedInType.js +1 -1
  32. package/lib/checks/managedWithoutKeys.js +1 -1
  33. package/lib/checks/nonexpandableStructured.js +5 -3
  34. package/lib/checks/nullableKeys.js +1 -1
  35. package/lib/checks/onConditions.js +5 -6
  36. package/lib/checks/parameters.js +1 -1
  37. package/lib/checks/queryNoDbArtifacts.js +2 -2
  38. package/lib/checks/selectItems.js +4 -4
  39. package/lib/checks/sql-snippets.js +4 -4
  40. package/lib/checks/types.js +7 -7
  41. package/lib/checks/utils.js +4 -4
  42. package/lib/checks/validator.js +16 -13
  43. package/lib/compiler/.eslintrc.json +1 -1
  44. package/lib/compiler/assert-consistency.js +0 -1
  45. package/lib/compiler/builtins.js +1 -1
  46. package/lib/compiler/checks.js +73 -15
  47. package/lib/compiler/define.js +3 -7
  48. package/lib/compiler/extend.js +212 -32
  49. package/lib/compiler/finalize-parse-cdl.js +7 -2
  50. package/lib/compiler/index.js +17 -14
  51. package/lib/compiler/populate.js +2 -5
  52. package/lib/compiler/propagator.js +2 -0
  53. package/lib/compiler/shared.js +23 -12
  54. package/lib/compiler/tweak-assocs.js +5 -6
  55. package/lib/compiler/utils.js +6 -0
  56. package/lib/edm/annotations/genericTranslation.js +553 -319
  57. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  58. package/lib/edm/csn2edm.js +88 -75
  59. package/lib/edm/edm.js +17 -3
  60. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  61. package/lib/edm/edmPreprocessor.js +106 -76
  62. package/lib/edm/edmUtils.js +41 -2
  63. package/lib/gen/Dictionary.json +34 -0
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +66 -63
  66. package/lib/gen/language.tokens +81 -81
  67. package/lib/gen/languageLexer.interp +4 -10
  68. package/lib/gen/languageLexer.js +854 -869
  69. package/lib/gen/languageLexer.tokens +79 -81
  70. package/lib/gen/languageParser.js +14360 -14146
  71. package/lib/inspect/inspectModelStatistics.js +2 -2
  72. package/lib/inspect/inspectPropagation.js +6 -6
  73. package/lib/inspect/inspectUtils.js +2 -2
  74. package/lib/json/from-csn.js +82 -40
  75. package/lib/json/to-csn.js +82 -157
  76. package/lib/language/.eslintrc.json +1 -4
  77. package/lib/language/genericAntlrParser.js +59 -38
  78. package/lib/language/language.g4 +1508 -1490
  79. package/lib/language/multiLineStringParser.js +1 -1
  80. package/lib/main.js +3 -3
  81. package/lib/model/csnUtils.js +130 -122
  82. package/lib/model/revealInternalProperties.js +1 -1
  83. package/lib/model/sortViews.js +4 -6
  84. package/lib/modelCompare/utils/filter.js +4 -3
  85. package/lib/optionProcessor.js +5 -0
  86. package/lib/render/DuplicateChecker.js +1 -1
  87. package/lib/render/manageConstraints.js +12 -12
  88. package/lib/render/toCdl.js +225 -159
  89. package/lib/render/toHdbcds.js +63 -63
  90. package/lib/render/toRename.js +5 -5
  91. package/lib/render/toSql.js +55 -65
  92. package/lib/render/utils/common.js +20 -37
  93. package/lib/render/utils/delta.js +3 -3
  94. package/lib/render/utils/sql.js +22 -6
  95. package/lib/render/utils/stringEscapes.js +3 -3
  96. package/lib/transform/db/applyTransformations.js +3 -3
  97. package/lib/transform/db/assertUnique.js +13 -12
  98. package/lib/transform/db/associations.js +5 -5
  99. package/lib/transform/db/cdsPersistence.js +10 -8
  100. package/lib/transform/db/constraints.js +14 -14
  101. package/lib/transform/db/expansion.js +20 -22
  102. package/lib/transform/db/flattening.js +24 -42
  103. package/lib/transform/db/groupByOrderBy.js +3 -3
  104. package/lib/transform/db/temporal.js +6 -6
  105. package/lib/transform/db/transformExists.js +23 -23
  106. package/lib/transform/db/views.js +16 -16
  107. package/lib/transform/draft/db.js +10 -10
  108. package/lib/transform/draft/odata.js +2 -2
  109. package/lib/transform/forOdataNew.js +12 -40
  110. package/lib/transform/forRelationalDB.js +16 -6
  111. package/lib/transform/localized.js +2 -2
  112. package/lib/transform/odata/toFinalBaseType.js +41 -27
  113. package/lib/transform/odata/typesExposure.js +106 -62
  114. package/lib/transform/parseExpr.js +209 -106
  115. package/lib/transform/transformUtilsNew.js +2 -2
  116. package/lib/transform/translateAssocsToJoins.js +24 -19
  117. package/lib/transform/universalCsn/coreComputed.js +10 -10
  118. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  119. package/lib/transform/universalCsn/utils.js +3 -3
  120. package/lib/utils/file.js +5 -5
  121. package/lib/utils/moduleResolve.js +13 -13
  122. package/lib/utils/objectUtils.js +6 -6
  123. package/lib/utils/term.js +5 -2
  124. package/lib/utils/timetrace.js +51 -24
  125. package/package.json +5 -7
  126. package/share/messages/check-proper-type-of.md +1 -1
  127. package/share/messages/message-explanations.json +1 -1
  128. package/share/messages/redirected-to-complex.md +4 -4
  129. 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, ['definitions', defName ]);
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, path) => {
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, path);
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, path) {
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("../base/error");
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: getUtils(csn), options, error };
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 "localized" is reserved for localization views');
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
- // If the definition('def' variable) is a type definition and the assigned type of this very same definition('def' variable)
27
- // is structured type, e.g.:
28
- //
29
- // type Struct1 {
30
- // a : Integer;
31
- // b : Integer;
32
- // };
33
- // type Struct2: Struct1;
34
- // after compilation the csn looks like this:
35
- // ...
36
- // "S.Struct1": {
37
- // "kind": "type",
38
- // "elements": {
39
- // "a": { "type": "cds.Integer" },
40
- // "b": { "type": "cds.Integer" }
41
- // } },
42
- // "S.Struct2": {
43
- // "kind": "type",
44
- // "type": "S.Struct1",
45
- // "elements": {
46
- // "a": { "type": "cds.Integer" },
47
- // "b": { "type": "cds.Integer" }
48
- // } } ...
49
- //
50
- // "S.Struct2" should looks just like "S.Struct1" => the "type": "S.Struct1" property has to be removed
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(node);
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 && node.items.type)
92
- ? getTypeNameInMultiSchema(node.type|| (node.items && node.items.type), serviceName)
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
- newType = createNewStructType(elements);
122
- // if using node enforces open/closed, set it on type
123
- if(node['@open'] !== undefined)
124
- newType['@open'] = node['@open']
125
- if (node.$location)
126
- setProp(newType, '$location', node.$location);
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
- csn.definitions[fullQualifiedNewTypeName] = newType;
129
- exposedTypes[fullQualifiedNewTypeName] = 1;
154
+ csn.definitions[fullQualifiedNewTypeName] = newType;
155
+ exposedTypes[fullQualifiedNewTypeName] = 1;
130
156
 
131
- // Recurse into elements of 'type' (if any) and expose them as well (is needed)
132
- newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
133
- if (node.elements && node.elements[elemName].$location)
134
- setProp(newElem, '$location', node.elements[elemName].$location);
135
- defName = typeDef.kind === 'type' ? typeName : defName;
136
- {
137
- const { isExposable, typeDef, typeName } = exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
138
- getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
139
- // if the type for the newElem was not exposed it may be a scalar type def from an external service that hasn't
140
- // been catched by expandToFinalBaseType() (forODataNew must not modify external imported services)
141
- if(!isExposable && isBuiltinType(typeName) && !isBuiltinType((newElem.items?.type || newElem.type))) {
142
- if(typeDef.items) {
143
- newElem.items = typeDef.items;
144
- delete newElem.type;
145
- }
146
- else if(newElem.items) {
147
- newElem.items.type = typeName;
148
- if(typeDef.enum)
149
- newElem.items.enum = typeDef.enum;
150
- }
151
- else {
152
- newElem.type = typeName;
153
- if(typeDef.enum)
154
- newElem.enum = typeDef.enum;
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
- copyAnnotations(typeDef, newType);
160
- // if the origin type had items, add items to exposed type
161
- if(typeDef.kind === 'type') {
162
- if(typeDef.items) {
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(node) {
229
+ function isTypeExposable() {
195
230
  let typeName = undefined;
196
231
  let typeDef = node;
197
- let elements = (node.items && node.items.elements || node.elements)
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 && node.items.type || node.type;
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) && !isArtifactInService(typeName, serviceName)) {
208
- while(!isBuiltinType(typeName)) {
209
- typeDef = csnUtils.getFinalTypeDef(typeName);
210
- if(typeDef) {
211
- if((rc.elements = (typeDef.items && typeDef.items.elements || typeDef.elements)) !== undefined)
212
- return rc;
213
- type = typeDef.items && typeDef.items.type || typeDef.type;
214
- typeName = (type.ref && csnUtils.getFinalType(type)) || type;
215
- }
216
- else {
217
- throw Error(`Please debug me: ${typeName} not found`);
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.elements && element.type || element.items && !element.items.elements && element.items.type);
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