@sap/cds-compiler 2.15.2 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +66 -1590
  2. package/bin/cdsc.js +42 -46
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +312 -143
  10. package/lib/api/options.js +15 -85
  11. package/lib/api/validate.js +6 -10
  12. package/lib/base/keywords.js +280 -110
  13. package/lib/base/message-registry.js +80 -24
  14. package/lib/base/messages.js +103 -52
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +53 -21
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +1 -1
  19. package/lib/checks/cdsPersistence.js +1 -0
  20. package/lib/checks/elements.js +6 -6
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/nonexpandableStructured.js +1 -1
  23. package/lib/checks/queryNoDbArtifacts.js +2 -1
  24. package/lib/checks/selectItems.js +5 -1
  25. package/lib/checks/types.js +4 -2
  26. package/lib/checks/utils.js +2 -2
  27. package/lib/checks/validator.js +2 -1
  28. package/lib/compiler/assert-consistency.js +15 -10
  29. package/lib/compiler/builtins.js +127 -10
  30. package/lib/compiler/define.js +6 -4
  31. package/lib/compiler/extend.js +63 -12
  32. package/lib/compiler/finalize-parse-cdl.js +20 -9
  33. package/lib/compiler/index.js +25 -11
  34. package/lib/compiler/moduleLayers.js +7 -0
  35. package/lib/compiler/populate.js +16 -14
  36. package/lib/compiler/propagator.js +3 -3
  37. package/lib/compiler/resolve.js +194 -222
  38. package/lib/compiler/shared.js +56 -76
  39. package/lib/compiler/tweak-assocs.js +9 -10
  40. package/lib/compiler/utils.js +7 -2
  41. package/lib/edm/annotations/genericTranslation.js +60 -6
  42. package/lib/edm/annotations/preprocessAnnotations.js +10 -11
  43. package/lib/edm/csn2edm.js +39 -41
  44. package/lib/edm/edm.js +22 -15
  45. package/lib/edm/edmPreprocessor.js +66 -69
  46. package/lib/edm/edmUtils.js +12 -62
  47. package/lib/gen/Dictionary.json +8 -6
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +8 -30
  50. package/lib/gen/language.tokens +105 -114
  51. package/lib/gen/languageLexer.interp +1 -34
  52. package/lib/gen/languageLexer.js +889 -1007
  53. package/lib/gen/languageLexer.tokens +95 -106
  54. package/lib/gen/languageParser.js +20717 -22376
  55. package/lib/json/from-csn.js +73 -68
  56. package/lib/json/to-csn.js +13 -10
  57. package/lib/language/antlrParser.js +2 -2
  58. package/lib/language/docCommentParser.js +61 -38
  59. package/lib/language/errorStrategy.js +52 -40
  60. package/lib/language/genericAntlrParser.js +333 -259
  61. package/lib/language/language.g4 +600 -645
  62. package/lib/language/multiLineStringParser.js +14 -42
  63. package/lib/language/textUtils.js +44 -0
  64. package/lib/main.d.ts +27 -42
  65. package/lib/main.js +104 -81
  66. package/lib/model/csnRefs.js +2 -1
  67. package/lib/model/csnUtils.js +183 -285
  68. package/lib/model/revealInternalProperties.js +32 -9
  69. package/lib/model/sortViews.js +32 -31
  70. package/lib/optionProcessor.js +64 -57
  71. package/lib/render/.eslintrc.json +1 -1
  72. package/lib/render/DuplicateChecker.js +4 -7
  73. package/lib/render/manageConstraints.js +70 -2
  74. package/lib/render/toCdl.js +334 -339
  75. package/lib/render/toHdbcds.js +20 -16
  76. package/lib/render/toRename.js +44 -22
  77. package/lib/render/toSql.js +60 -54
  78. package/lib/render/utils/common.js +15 -1
  79. package/lib/render/utils/sql.js +20 -19
  80. package/lib/sql-identifier.js +6 -0
  81. package/lib/transform/db/.eslintrc.json +3 -2
  82. package/lib/transform/db/cdsPersistence.js +5 -15
  83. package/lib/transform/db/constraints.js +1 -1
  84. package/lib/transform/db/expansion.js +7 -6
  85. package/lib/transform/db/flattening.js +18 -19
  86. package/lib/transform/db/views.js +3 -3
  87. package/lib/transform/draft/.eslintrc.json +2 -2
  88. package/lib/transform/draft/db.js +6 -6
  89. package/lib/transform/draft/odata.js +6 -7
  90. package/lib/transform/forHanaNew.js +19 -22
  91. package/lib/transform/forOdataNew.js +13 -15
  92. package/lib/transform/localized.js +35 -25
  93. package/lib/transform/odata/toFinalBaseType.js +11 -9
  94. package/lib/transform/odata/typesExposure.js +3 -3
  95. package/lib/transform/odata/utils.js +1 -38
  96. package/lib/transform/transformUtilsNew.js +63 -77
  97. package/lib/transform/translateAssocsToJoins.js +6 -2
  98. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  99. package/lib/transform/universalCsn/coreComputed.js +11 -6
  100. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  101. package/lib/utils/file.js +31 -21
  102. package/lib/utils/timetrace.js +20 -21
  103. package/package.json +34 -4
  104. package/share/messages/syntax-expected-integer.md +9 -8
  105. package/doc/ApiMigration.md +0 -237
  106. package/doc/CommandLineMigration.md +0 -58
  107. package/doc/ErrorMessages.md +0 -175
  108. package/doc/FioriAnnotations.md +0 -94
  109. package/doc/ODataTransformation.md +0 -273
  110. package/lib/backends.js +0 -529
  111. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -3,8 +3,7 @@
3
3
  const { makeMessageFunction } = require('../base/messages');
4
4
  const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
5
5
  const transformUtils = require('./transformUtilsNew');
6
- const { getUtils,
7
- cloneCsnNonDict,
6
+ const { cloneCsnNonDict,
8
7
  forEachDefinition,
9
8
  forEachMemberRecursively,
10
9
  applyTransformationsOnNonDictionary,
@@ -86,10 +85,10 @@ function transform4odataWithCsn(inputModel, options) {
86
85
  extractValidFromToKeyElement,
87
86
  checkAssignment, checkMultipleAssignments,
88
87
  recurseElements, setAnnotation, renameAnnotation,
89
- expandStructsInExpression
88
+ expandStructsInExpression,
89
+ csnUtils,
90
90
  } = transformers;
91
91
 
92
- const csnUtils = getUtils(csn);
93
92
  const {
94
93
  getCsnDef,
95
94
  getServiceName,
@@ -98,11 +97,10 @@ function transform4odataWithCsn(inputModel, options) {
98
97
  inspectRef,
99
98
  artifactRef,
100
99
  effectiveType,
101
- getFinalBaseType,
102
100
  } = csnUtils;
103
101
 
104
102
  // are we working with structured OData or not
105
- const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
103
+ const structuredOData = options.odataFormat === 'structured' && options.odataVersion === 'v4';
106
104
 
107
105
  // collect all declared non-abstract services from the model
108
106
  // use the array when there is a need to identify if an artifact is in a service or not
@@ -115,17 +113,17 @@ function transform4odataWithCsn(inputModel, options) {
115
113
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
116
114
  enrichUniversalCsn(csn, options);
117
115
 
118
- const keepLocalizedViews = isDeprecatedEnabled(options, 'createLocalizedViews');
116
+ const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
119
117
 
120
118
  function acceptLocalizedView(_name, parent) {
121
119
  csn.definitions[parent].$localized = true;
122
120
  return keepLocalizedViews && !isExternalServiceMember(undefined, parent);
123
121
  }
124
122
 
125
- addLocalizationViews(csn, options, acceptLocalizedView);
123
+ addLocalizationViews(csn, options, { acceptLocalizedView, ignoreUnknownExtensions: true });
126
124
 
127
125
  const cleanup = validate.forOdata(csn, {
128
- message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
126
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
129
127
  });
130
128
 
131
129
 
@@ -214,16 +212,16 @@ function transform4odataWithCsn(inputModel, options) {
214
212
 
215
213
  // Annotate artifacts with their DB names if requested.
216
214
  // Skip artifacts that have no DB equivalent anyway
217
- if (options.toOdata.names && !(def.kind in skipPersNameKinds))
218
- def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.toOdata.names, csn);
215
+ if (options.sqlMapping && !(def.kind in skipPersNameKinds))
216
+ def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
219
217
 
220
218
  forEachMemberRecursively(def, (member, memberName, propertyName) => {
221
219
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested
222
220
  // Only these are actually required and don't annotate virtual elements in entities or types
223
221
  // as they have no DB representation (although in views)
224
- if (options.toOdata.names && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
222
+ if (options.sqlMapping && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
225
223
  // If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
226
- member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.toOdata.names);
224
+ member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
227
225
  }
228
226
 
229
227
  // Mark fields with @odata.on.insert/update as @Core.Computed
@@ -326,7 +324,7 @@ function transform4odataWithCsn(inputModel, options) {
326
324
  // but '@Core.Immutable' for everything else.
327
325
  if (!(node['@readonly'] && node['@insertonly'])) {
328
326
  if (name === '@readonly' && node[name] !== null) {
329
- if (node.kind === 'entity') {
327
+ if (node.kind === 'entity' || node.kind === 'aspect') {
330
328
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
331
329
  setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
332
330
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -336,7 +334,7 @@ function transform4odataWithCsn(inputModel, options) {
336
334
  }
337
335
  // @insertonly is effective on entities/queries only
338
336
  else if (name === '@insertonly' && node[name] !== null) {
339
- if (node.kind === 'entity') {
337
+ if (node.kind === 'entity' || node.kind === 'aspect') {
340
338
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
341
339
  setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
342
340
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const { makeMessageFunction } = require('../base/messages');
4
- const { setProp } = require('../base/model');
4
+ const { setProp, isDeprecatedEnabled} = require('../base/model');
5
5
  const { hasErrors } = require('../base/messages');
6
- const { cloneCsnDictionary, applyDefinitionAnnotationsFromExtensions} = require('../model/csnUtils');
6
+ const { forEachKey } = require('../utils/objectUtils');
7
7
  const { cleanSymbols } = require('../base/cleanSymbols.js');
8
8
  const {
9
+ cloneCsnDictionary,
9
10
  cloneCsnNonDict,
11
+ applyAnnotationsFromExtensions,
10
12
  forEachDefinition,
11
13
  forEachGeneric,
12
14
  forAllQueries,
@@ -67,9 +69,9 @@ const _targetFor = Symbol('_targetFor');
67
69
  * @param {CSN.Options} options
68
70
  * @param {boolean} useJoins If true, rewrite the "localized" association to a
69
71
  * join in direct convenience views.
70
- * @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
72
+ * @param {object} config
71
73
  */
72
- function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = null) {
74
+ function _addLocalizationViews(csn, options, useJoins, config) {
73
75
  // Don't try to create convenience views with errors.
74
76
  if (hasErrors(options.messages))
75
77
  return csn;
@@ -78,6 +80,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
78
80
  if (hasExistingLocalizationViews(csn, options, messageFunctions))
79
81
  return csn;
80
82
 
83
+ const { acceptLocalizedView, ignoreUnknownExtensions } = config;
81
84
  const noCoalesce = (options.localizedLanguageFallback === 'none' ||
82
85
  options.localizedWithoutCoalesce);
83
86
 
@@ -87,9 +90,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
87
90
  forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
88
91
 
89
92
  // In case that the user tried to annotate `localized.*` artifacts, apply them.
90
- applyDefinitionAnnotationsFromExtensions(csn, {
91
- overwrite: true,
92
- filter: (name) => name.startsWith('localized.')
93
+ applyAnnotationsFromExtensions(csn, {
94
+ override: true,
95
+ filter: (name) => name.startsWith('localized.'),
96
+ notFound(name, index) {
97
+ if (!ignoreUnknownExtensions)
98
+ messageFunctions.message('anno-undefined-art', [ 'extensions', index ], { name })
99
+ },
93
100
  });
94
101
 
95
102
  sortCsnDefinitionsForTests(csn, options);
@@ -147,7 +154,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
147
154
  else
148
155
  view = createLocalizedViewForEntity(art, artName, textElements);
149
156
 
150
- copyPersistenceAnnotations(view, art);
157
+ copyPersistenceAnnotations(view, art, options);
151
158
  csn.definitions[viewName] = view;
152
159
  }
153
160
 
@@ -605,10 +612,10 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
605
612
  *
606
613
  * @param {CSN.Model} csn
607
614
  * @param {CSN.Options} options
608
- * @param [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
615
+ * @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
609
616
  */
610
- function addLocalizationViews(csn, options, acceptLocalizedView = null) {
611
- return _addLocalizationViews(csn, options, false, acceptLocalizedView);
617
+ function addLocalizationViews(csn, options, config = {}) {
618
+ return _addLocalizationViews(csn, options, false, config);
612
619
  }
613
620
 
614
621
  /**
@@ -618,10 +625,10 @@ function addLocalizationViews(csn, options, acceptLocalizedView = null) {
618
625
  *
619
626
  * @param {CSN.Model} csn
620
627
  * @param {CSN.Options} options
621
- * @param [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
628
+ * @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
622
629
  */
623
- function addLocalizationViewsWithJoins(csn, options, acceptLocalizedView = null) {
624
- return _addLocalizationViews(csn, options, true, acceptLocalizedView);
630
+ function addLocalizationViewsWithJoins(csn, options, config = {}) {
631
+ return _addLocalizationViews(csn, options, true, config);
625
632
  }
626
633
 
627
634
  /**
@@ -667,21 +674,24 @@ function copyLocation(target, source) {
667
674
  }
668
675
 
669
676
  /**
670
- * Copy some @cds.persistence.* annotations from the source to
671
- * the target. Ignores existing annotations on the target.
677
+ * Copy @cds.persistence.exists/skip annotations from the source to
678
+ * the target. Ignores existing annotations on the _target_.
672
679
  *
673
680
  * @param {CSN.Artifact} target
674
681
  * @param {CSN.Artifact} source
682
+ * @param {CSN.Options} options
675
683
  */
676
- function copyPersistenceAnnotations(target, source) {
677
- Object.keys(source)
678
- .forEach(anno => {
679
- // Do NOT copy ".exists" at the moment. ".exists" is not propagated
680
- // and this would lead to some localization views referencing not-existing
681
- // "localized.XYZ" views.
682
- if (anno === '@cds.persistence.skip')
683
- target[anno] = source[anno];
684
- });
684
+ function copyPersistenceAnnotations(target, source, options) {
685
+ const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
686
+ forEachKey(source, anno => {
687
+ // Note:
688
+ // Because `.exists` is copied to the convenience view, it could
689
+ // lead to some localization views referencing non-existing ones.
690
+ // But that is the contract: User says that it already exists!
691
+ // In v2, `.exists` was never copied.
692
+ if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
693
+ target[anno] = source[anno];
694
+ });
685
695
  }
686
696
 
687
697
  /**
@@ -7,7 +7,7 @@ const {
7
7
  const { isArtifactInSomeService, isArtifactInService } = require('./utils');
8
8
 
9
9
  function expandToFinalBaseType(csn, transformers, csnUtils, services, options, isExternalServiceMember) {
10
- const isV4 = options.toOdata.version === 'v4';
10
+ const isV4 = options.odataVersion === 'v4';
11
11
  forEachDefinition(csn, (def, defName) => {
12
12
  // Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
13
13
  forEachMemberRecursively(def, (member) => {
@@ -87,9 +87,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
87
87
  if (node.kind === 'event') return;
88
88
 
89
89
  // elements have precedence over type
90
- if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
90
+ if (node.type && (!isBuiltinType(node.type) && isExpandable(node, defName) || node.kind === 'type')) {
91
91
  // 1. Get the final type of the node (resolve derived type chain)
92
- const finalType = csnUtils.getFinalBaseType(node.type);
92
+ const finalType = csnUtils.getFinalBaseTypeWithProps(node.type);
93
93
  if (finalType) {
94
94
  // The type replacement depends on whether 'node' is a definition or a member[element].
95
95
  if (node.kind) {
@@ -101,7 +101,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
101
101
  // type A: B; -> {...}
102
102
  // type B: C; -> { ... }
103
103
  // type C { .... };
104
- if (isBuiltinType(finalType)) {
104
+ if (isBuiltinType(finalType.type)) {
105
105
  // use transformUrilsNew::toFinalBaseType for the moment,
106
106
  // as it is collects along the chain of types
107
107
  // attributes that need to be propagated
@@ -129,8 +129,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
129
129
  // type A: B; -> {...}
130
130
  // type B: C; -> { ... }
131
131
  // type C { .... };
132
- if (isBuiltinType(finalType)) {
133
- // use transformUrilsNew::toFinalBaseType for the moment,
132
+ if (isBuiltinType(finalType.type)) {
133
+ // use transformUtilsNew::toFinalBaseType for the moment,
134
134
  // as it is collects along the chain of types
135
135
  // attributes that need to be propagated
136
136
  // enum, length, scale, etc.
@@ -148,7 +148,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
148
148
  // example in actions: 'action act() return Primitive; type Primitive: array of String;'
149
149
  const currService = csnUtils.getServiceName(defName);
150
150
  const finalType = csnUtils.getFinalTypeDef(node.type);
151
- if (finalType.items && isBuiltinType(finalType.items.type)) {
151
+ if (finalType.items &&
152
+ (isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
153
+ {
152
154
  if (!isArtifactInService(node.type, currService) || !isV4) {
153
155
  node.items = finalType.items;
154
156
  delete node.type;
@@ -184,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
184
186
  function isUserDefinedBuiltinFromTheCurrService(node, defName) {
185
187
  // in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
186
188
  // a builtin from the service - do not expand to the final base type
187
- let finalBaseType = csnUtils.getFinalBaseType(node.type);
188
- // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseType(finalBaseType.items);
189
+ let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
190
+ // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
189
191
  const currService = csnUtils.getServiceName(defName);
190
192
  return node.type && !node.type.ref
191
193
  && isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
@@ -14,7 +14,7 @@ const { copyAnnotations } = require('../../model/csnUtils');
14
14
  function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
15
15
  const { error } = message;
16
16
  // are we working with OData proxies or cross-service refs
17
- const isMultiSchema = options.toOdata.version === 'v4' && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs);
17
+ const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.odataXServiceRefs);
18
18
  // collect in this variable all the newly exposed types
19
19
  const schemas = Object.create(null);
20
20
  const exposedTypes = Object.create(null);
@@ -122,7 +122,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
122
122
  if (node.elements && node.elements[elemName].$location)
123
123
  setProp(newElem, '$location', node.elements[elemName].$location);
124
124
  defName = typeDef.kind === 'type' ? typeName : defName;
125
- exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
125
+ exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
126
126
  getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
127
127
  });
128
128
  copyAnnotations(typeDef, newType);
@@ -151,7 +151,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
151
151
  * 1) If it's an anonymous structured type (items.elements || elements)
152
152
  * 2) If it's a named type resolve to the final type definition and
153
153
  * check if this is a structured type
154
- * Returns an object that indicates
154
+ * Returns an object that indicates
155
155
  * - wether or not the type needs exposure
156
156
  * - the elements dictionary that needs to be cloned
157
157
  * - the typeDef, either the resolved type def or the node itself
@@ -1,38 +1,4 @@
1
- const {
2
- forEachDefinition,
3
- forEachMemberRecursively,
4
- } = require('../../model/csnUtils');
5
-
6
-
7
- // Return true if 'artifact' has an association type
8
- function isAssociation(artifact) {
9
- return (artifact.type === 'cds.Association' || artifact.type === 'Association');
10
- }
11
-
12
- // Return true if 'artifact' has a composition type
13
- function isComposition(artifact) {
14
- return (artifact.type === 'cds.Composition' || artifact.type === 'Composition')
15
- }
16
-
17
- function isAssociationOrComposition(artifact) {
18
- return isAssociation(artifact) || isComposition(artifact);
19
- }
20
-
21
- function isManagedAssociation(artifact) {
22
- return artifact.target !== undefined && artifact.on === undefined;
23
- }
24
-
25
- function forEachManagedAssociation(csn, callback, isExternalServiceMember) {
26
-
27
- forEachDefinition(csn, (def) => {
28
- forEachMemberRecursively(def, (element) => {
29
- if (isAssociationOrComposition(element) && !element.on) {
30
- callback(element)
31
- }
32
- })
33
- }, { skipArtifact: isExternalServiceMember });
34
-
35
- }
1
+ 'use strict';
36
2
 
37
3
  /**
38
4
  * Return the definition name, without the prefixed service name
@@ -87,12 +53,9 @@ function isLocalizedArtifactInService(artName, services) {
87
53
  }
88
54
 
89
55
  module.exports = {
90
- forEachManagedAssociation,
91
56
  defNameWithoutServiceOrContextName,
92
57
  getServiceOfArtifact,
93
58
  isArtifactInService,
94
59
  isArtifactInSomeService,
95
- isAssociationOrComposition,
96
60
  isLocalizedArtifactInService,
97
- isManagedAssociation,
98
61
  }
@@ -6,10 +6,10 @@
6
6
 
7
7
  const { hasErrors, makeMessageFunction } = require('../base/messages');
8
8
  const { setProp } = require('../base/model');
9
- const { csnRefs } = require('../model/csnRefs');
10
9
 
11
10
  const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
12
- const { cloneCsnNonDict, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
11
+ const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
12
+ const { typeParameters, isBuiltinType } = require('../compiler/builtins');
13
13
  const { ModelError } = require("../base/error");
14
14
  const { forEach } = require('../utils/objectUtils');
15
15
 
@@ -20,20 +20,18 @@ const { forEach } = require('../utils/objectUtils');
20
20
  // TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
21
21
  function getTransformers(model, options, pathDelimiter = '_') {
22
22
  const { error, warning, info } = makeMessageFunction(model, options);
23
+ const csnUtils = getUtils(model);
23
24
  const {
24
25
  getCsnDef,
25
- getFinalBaseType,
26
+ getFinalBaseTypeWithProps,
26
27
  hasAnnotationValue,
27
28
  inspectRef,
28
29
  isStructured,
29
- } = getUtils(model);
30
-
31
- const {
32
30
  effectiveType,
33
- } = csnRefs(model);
34
-
31
+ } = csnUtils;
35
32
 
36
33
  return {
34
+ csnUtils,
37
35
  resolvePath,
38
36
  flattenPath,
39
37
  addDefaultTypeFacets,
@@ -45,7 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
45
43
  copyTypeProperties,
46
44
  isAssociationOperand,
47
45
  isDollarSelfOrProjectionOperand,
48
- getFinalBaseType,
49
46
  createExposingProjection,
50
47
  createAndAddDraftAdminDataProjection,
51
48
  createScalarElement,
@@ -243,21 +240,19 @@ function getTransformers(model, options, pathDelimiter = '_') {
243
240
  // for type of 'x' -> elem.type is an object, not a string -> use directly
244
241
  let elemType;
245
242
  if (!elem.elements) // structures do not have final base type
246
- elemType = getFinalBaseType(elem.type);
243
+ elemType = getFinalBaseTypeWithProps(elem.type);
247
244
 
248
245
  const struct = elemType ? elemType.elements : elem.elements;
249
246
 
250
247
  // Collect all child elements (recursively) into 'result'
251
248
  // TODO: Do not report collisions in the generated elements here, but instead
252
- // leave that work to the receiver of this result
249
+ // leave that work to the receiver of this result
253
250
  let result = Object.create(null);
254
251
  const addGeneratedFlattenedElement = (e, eName) => {
255
- if(result[eName]){
256
- error(null, pathInCsn, { name: eName },
257
- 'Generated element $(NAME) conflicts with other generated element')
258
- } else {
252
+ if (result[eName])
253
+ error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
254
+ else
259
255
  result[eName] = e;
260
- }
261
256
  }
262
257
  forEach(struct, (childName, childElem) => {
263
258
  if (isStructured(childElem)) {
@@ -287,7 +282,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
287
282
 
288
283
  // Fix all collected flat elements (names, annotations, properties, origin ..)
289
284
  forEach(result, (name, flatElem) => {
290
- // Copy annotations from struct (not overwriting, because deep annotations should have precedence)
285
+ // Copy annotations from struct (not overwriting, because deep annotations should have precedence).
286
+ // Attention:
287
+ // This has historic reasons. We don't copy doc-comments because copying annotations
288
+ // is questionable to begin with. Only selected annotations should have been copied,
289
+ // if at all.
291
290
  copyAnnotations(elem, flatElem, false);
292
291
  // Copy selected type properties
293
292
  const props = ['key', 'virtual', 'masked', 'viaAll'];
@@ -395,78 +394,65 @@ function getTransformers(model, options, pathDelimiter = '_') {
395
394
  }
396
395
 
397
396
  /**
398
- * Replace the type of 'node' with its final base type (in contrast to the compiler,
399
- * also unravel derived enum types, i.e. take the final base type of the enum's base type.
400
- * Similar with associations and compositions (we probably need a _baseType link)
397
+ * Replace the type of 'nodeWithType' with its final base type, i.e. copy relevant type properties and
398
+ * set the `type` property to the builtin if scalar or delete it if structured/arrayed.
401
399
  *
402
- * @param {CSN.Artifact} node
400
+ * @param {object} nodeWithType
403
401
  * @param {WeakMap} [resolved] WeakMap containing already resolved refs
404
402
  * @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
405
- * @returns {void}
406
403
  */
407
- function toFinalBaseType(node, resolved, keepLocalized=false) {
408
- // Nothing to do if no type (or if array/struct type)
409
- if (!node || !node.type) return;
410
- // In case of a ref -> Follow the ref
411
- if (node.type && node.type.ref) {
412
- const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
413
- if(finalBaseType === null)
414
- throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
415
- if(finalBaseType.elements) {
416
- // This changes the order - to be discussed!
417
- node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
418
- delete node.type; // delete the type reference as edm processing does not expect it
419
- } else if(finalBaseType.items) {
420
- // This changes the order - to be discussed!
421
- node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
422
- delete node.type;
423
- } else {
424
- node.type=finalBaseType;
425
- }
404
+ function toFinalBaseType(nodeWithType, resolved = new WeakMap(), keepLocalized = false) {
405
+ const type = nodeWithType?.type;
406
+ if (!type || nodeWithType.elements || nodeWithType.items || resolved.has(nodeWithType)) {
426
407
  return;
427
408
  }
428
- // .. or builtin already
429
- if (node.type && isBuiltinType(node.type)) return;
409
+ // The caller may use `{ art }` syntax for `{ ref }` objects, but we only use
410
+ // it to indicate that an artifact has been processed.
411
+ resolved.set(nodeWithType, nodeWithType);
430
412
 
431
- // The type might already be a full fledged type def (array of)
432
- let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
433
- // Nothing to do if type is an array or a struct type
434
- if (typeDef.items || typeDef.elements) {
435
- // cloneCsn only works correctly if we start "from the top"
436
- const cloneTypeDef = cloneCsnNonDict(typeDef, options);
437
- // With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
438
- if(typeDef.items) {
439
- delete node.type;
440
- if(!node.items)
441
- Object.assign(node, {items: cloneTypeDef.items});
442
- }
443
- if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
444
- if(!typeDef.items)
445
- delete node.type;
446
- if(!node.elements)
447
- Object.assign(node, {elements: cloneTypeDef.elements});
448
- }
413
+ // Nothing to copy from builtin.
414
+ if (typeof type === 'string' && isBuiltinType(type))
415
+ return;
449
416
 
417
+ let typeRef = null;
418
+ if (resolved.has(type)) {
419
+ typeRef = resolved.get(type)?.art
420
+ // The cached entry may not be resolved, yet.
421
+ if (typeRef.type && !isBuiltinType(typeRef.type))
422
+ typeRef = getFinalBaseTypeWithProps(typeRef.type);
423
+ } else {
424
+ typeRef = getFinalBaseTypeWithProps(type);
425
+ }
450
426
 
427
+ if (typeRef.elements || typeRef.items) {
428
+ // Copy elements/items and we're finished. No need to look up actual base type,
429
+ // since it must also be structured and must contain at least as many elements,
430
+ // if not more (in client style CSN).
431
+ if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
432
+ nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
433
+ delete nodeWithType.type;
434
+ }
435
+ if (typeRef.items) {
436
+ nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
437
+ delete nodeWithType.type;
438
+ }
451
439
  return;
452
440
  }
453
- // if the declared element is an enum, these values are with priority
454
- if (!node.enum && typeDef.enum) {
455
- const clone = cloneCsnDictionary(typeDef.enum, options);
456
- Object.assign(node, { enum: clone });
441
+
442
+ if (typeRef.enum && nodeWithType.enum === undefined)
443
+ nodeWithType.enum = cloneCsnDictionary(typeRef.enum, options);
444
+
445
+ // Copy type and type arguments (+ localized)
446
+
447
+ for (const param of typeParameters.list) {
448
+ if (nodeWithType[param] === undefined && typeRef[param] !== undefined &&!typeRef.$default) {
449
+ nodeWithType[param] = typeRef[param];
450
+ }
457
451
  }
458
- if (node.length === undefined && typeDef.length !== undefined)
459
- Object.assign(node, { length: typeDef.length });
460
- if (node.precision === undefined && typeDef.precision !== undefined)
461
- Object.assign(node, { precision: typeDef.precision });
462
- if (node.scale === undefined && typeDef.scale !== undefined)
463
- Object.assign(node, { scale: typeDef.scale });
464
- if (node.srid === undefined && typeDef.srid !== undefined)
465
- Object.assign(node, { srid: typeDef.srid });
466
- if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
467
- Object.assign(node, { localized: typeDef.localized });
468
- node.type = typeDef.type;
469
- toFinalBaseType(node);
452
+ if (keepLocalized && nodeWithType.localized === undefined && typeRef.localized !== undefined)
453
+ nodeWithType.localized = typeRef.localized;
454
+ if (typeRef.type)
455
+ nodeWithType.type = typeRef.type;
470
456
  }
471
457
 
472
458
  // Return a full projection 'projectionId' of artifact 'art' for exposure in 'service'.
@@ -57,10 +57,10 @@ function translateAssocsToJoins(model, inputOptions = {})
57
57
  const options = model.options || inputOptions;
58
58
 
59
59
  // create JOINs for foreign key paths
60
- const noJoinForFK = options.forHana ? !options.forHana.joinfk : true;
60
+ const noJoinForFK = options.forHana ? !options.joinfk : true;
61
61
 
62
62
  // Note: This is called from the 'forHana' transformations, so it is controlled by its options)
63
- const pathDelimiter = (options.forHana && options.forHana.names === 'hdbcds') ? '.' : '_';
63
+ const pathDelimiter = (options.forHana && options.sqlMapping === 'hdbcds') ? '.' : '_';
64
64
 
65
65
  forEachDefinition(model, prepareAssociations);
66
66
  forEachDefinition(model, transformQueries);
@@ -1818,6 +1818,10 @@ function walkPath(node, env)
1818
1818
  // or that are parameters ($parameters or escaped paths (':')
1819
1819
  //path.length && path[ path.length-1 ]._artifact
1820
1820
  const art = path && path.length && path[path.length-1]._artifact;
1821
+
1822
+ // regardless of the position in the query, ignore paths that have virtual path steps
1823
+ if(art && path.some(ps => ps._artifact && ps._artifact.virtual && ps._artifact.virtual.val))
1824
+ return path;
1821
1825
  if(art && !internalArtifactKinds.includes(art.kind))
1822
1826
  {
1823
1827
  if(env.callback)
@@ -21,11 +21,11 @@
21
21
  "sonarjs/no-duplicate-string": "off"
22
22
  },
23
23
  "parserOptions": {
24
- "ecmaVersion": 2018,
24
+ "ecmaVersion": 2020,
25
25
  "sourceType": "script"
26
26
  },
27
27
  "env": {
28
- "es6": true,
28
+ "es2020": true,
29
29
  "node": true
30
30
  },
31
31
  "settings": {
@@ -12,7 +12,7 @@ const { setAnnotationIfNotDefined } = require('./utils');
12
12
  */
13
13
  function setCoreComputedOnViews(csn) {
14
14
  const {
15
- artifactRef, getColumn, getElement,
15
+ artifactRef, getColumn, getElement, getOrigin,
16
16
  } = getUtils(csn, 'init-all');
17
17
 
18
18
  forEachDefinition(csn, (artifact) => {
@@ -57,7 +57,15 @@ function setCoreComputedOnViews(csn) {
57
57
  const column = getColumn(element);
58
58
  if (column)
59
59
  return column;
60
- return getElementFromFrom(name, base.from);
60
+ const from = getElementFromFrom(name, base.from);
61
+ if (from)
62
+ return from;
63
+ // For .expand/.inline, we can find it via origin
64
+ // Although I would have expected to find it via getColumn...
65
+ const origin = getOrigin(element);
66
+ if (origin)
67
+ return origin;
68
+ throw new Error(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
61
69
  }
62
70
 
63
71
  /**
@@ -87,10 +95,7 @@ function setCoreComputedOnViews(csn) {
87
95
  return getElementFromFrom(name, base.SET.args[0]);
88
96
  }
89
97
  else if (base.args && base.join) {
90
- const result = checkJoinSources(base.args, name);
91
- if (!result)
92
- throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
93
- return result;
98
+ return checkJoinSources(base.args, name);
94
99
  }
95
100
 
96
101
  throw new Error(JSON.stringify(base));