@sap/cds-compiler 4.7.6 → 4.8.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 (93) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/bin/cdsc.js +15 -1
  3. package/bin/cdshi.js +13 -3
  4. package/doc/CHANGELOG_BETA.md +5 -1
  5. package/lib/api/main.js +61 -23
  6. package/lib/api/options.js +40 -0
  7. package/lib/base/builtins.js +89 -0
  8. package/lib/base/keywords.js +5 -1
  9. package/lib/base/location.js +91 -14
  10. package/lib/base/message-registry.js +50 -33
  11. package/lib/base/messages.js +71 -16
  12. package/lib/base/model.js +0 -2
  13. package/lib/checks/actionsFunctions.js +1 -1
  14. package/lib/checks/elements.js +2 -1
  15. package/lib/checks/enricher.js +2 -2
  16. package/lib/checks/queryNoDbArtifacts.js +2 -1
  17. package/lib/checks/utils.js +1 -1
  18. package/lib/checks/validator.js +6 -22
  19. package/lib/compiler/assert-consistency.js +3 -5
  20. package/lib/compiler/builtins.js +0 -74
  21. package/lib/compiler/checks.js +61 -11
  22. package/lib/compiler/define.js +3 -3
  23. package/lib/compiler/extend.js +2 -2
  24. package/lib/compiler/index.js +9 -9
  25. package/lib/compiler/populate.js +13 -5
  26. package/lib/compiler/propagator.js +3 -0
  27. package/lib/compiler/resolve.js +6 -20
  28. package/lib/compiler/shared.js +1 -1
  29. package/lib/compiler/tweak-assocs.js +2 -2
  30. package/lib/compiler/utils.js +3 -3
  31. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  32. package/lib/edm/annotations/edmJson.js +7 -5
  33. package/lib/edm/annotations/genericTranslation.js +113 -55
  34. package/lib/edm/csn2edm.js +25 -9
  35. package/lib/edm/edm.js +3 -3
  36. package/lib/edm/edmInboundChecks.js +24 -5
  37. package/lib/edm/edmPreprocessor.js +46 -20
  38. package/lib/edm/edmUtils.js +3 -16
  39. package/lib/gen/Dictionary.json +9 -0
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +1941 -1850
  43. package/lib/json/csnVersion.js +7 -4
  44. package/lib/json/from-csn.js +8 -7
  45. package/lib/json/to-csn.js +12 -7
  46. package/lib/language/antlrParser.js +1 -1
  47. package/lib/language/genericAntlrParser.js +9 -10
  48. package/lib/language/multiLineStringParser.js +2 -2
  49. package/lib/language/textUtils.js +1 -1
  50. package/lib/main.d.ts +23 -0
  51. package/lib/main.js +8 -1
  52. package/lib/model/cloneCsn.js +15 -6
  53. package/lib/model/csnRefs.js +141 -35
  54. package/lib/model/csnUtils.js +1 -4
  55. package/lib/model/enrichCsn.js +1 -1
  56. package/lib/modelCompare/compare.js +106 -92
  57. package/lib/optionProcessor.js +23 -1
  58. package/lib/render/toCdl.js +3 -2
  59. package/lib/render/toHdbcds.js +4 -48
  60. package/lib/render/toSql.js +6 -3
  61. package/lib/transform/addTenantFields.js +58 -35
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/expansion.js +3 -0
  64. package/lib/transform/db/flattening.js +71 -46
  65. package/lib/transform/db/views.js +1 -4
  66. package/lib/transform/effective/main.js +6 -3
  67. package/lib/transform/effective/misc.js +18 -8
  68. package/lib/transform/effective/types.js +4 -3
  69. package/lib/transform/forOdata.js +8 -7
  70. package/lib/transform/forRelationalDB.js +103 -112
  71. package/lib/transform/odata/flattening.js +82 -44
  72. package/lib/transform/odata/toFinalBaseType.js +9 -25
  73. package/lib/transform/odata/typesExposure.js +28 -15
  74. package/lib/transform/parseExpr.js +0 -3
  75. package/lib/transform/transformUtils.js +12 -8
  76. package/lib/transform/translateAssocsToJoins.js +2 -2
  77. package/lib/transform/universalCsn/coreComputed.js +2 -1
  78. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
  79. package/package.json +2 -2
  80. package/share/messages/README.md +4 -0
  81. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  82. package/share/messages/check-proper-type-of.md +1 -1
  83. package/share/messages/def-duplicate-autoexposed.md +1 -1
  84. package/share/messages/extend-repeated-intralayer.md +3 -16
  85. package/share/messages/extend-unrelated-layer.md +1 -1
  86. package/share/messages/message-explanations.json +1 -0
  87. package/share/messages/redirected-to-ambiguous.md +1 -1
  88. package/share/messages/redirected-to-complex.md +1 -1
  89. package/share/messages/redirected-to-unrelated.md +1 -1
  90. package/share/messages/rewrite-not-supported.md +1 -1
  91. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  92. package/share/messages/type-missing-enum-value.md +59 -0
  93. package/share/messages/wildcard-excluding-one.md +1 -1
@@ -6,9 +6,9 @@ const {
6
6
  forEachDefinition,
7
7
  forEachGeneric,
8
8
  forEachMemberRecursively,
9
- isBuiltinType,
10
- findAnnotationExpression,
9
+ findAnnotationExpression,
11
10
  } = require('../../model/csnUtils');
11
+ const { isBuiltinType } = require('../../base/builtins');
12
12
  const { isArtifactInSomeService, isArtifactInService } = require('./utils');
13
13
  const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
14
14
 
@@ -98,12 +98,6 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
98
98
 
99
99
  function expandToFinalBaseType(node, defName) {
100
100
  if (!node) return;
101
- // TODO: Clarify how should events be handled?
102
- // They are not treated by the transformUtils::toFinalBaseType function
103
- // in the same manner as named types, because the elements of structured events are not
104
- // propagated as it is with types.
105
- // It is ok to skip the expansion to the final base type for now as events are not rendered in
106
- // EDMX at the moment and the reference in the OData CSN is fulfilled.
107
101
  if (node.kind === 'event') return;
108
102
 
109
103
  if(node.type && !isBuiltinType(node.type)) {
@@ -199,7 +193,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
199
193
  // do the clone only if really needed
200
194
  if((finalBaseType.items && !node.items) ||
201
195
  (finalBaseType.elements && !node.elements)) {
202
- // use the 'real' type, don't clone a clone
196
+ // clone the definition not another clone
203
197
  let _type = node._type;
204
198
  while(_type._type && !_type.items && !_type.elements)
205
199
  _type = _type._type;
@@ -227,15 +221,10 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
227
221
  const typeRefStr = f(node.type.ref);
228
222
  const typeRefRootPath = $path2path(typeRefCsnPath);
229
223
  forEachMemberRecursively(clone, (elt, eltName, prop, location) => {
230
- const [ xprANames /*nxprANames */ ] = Object.keys(elt).reduce((acc, pn) => {
231
- if (pn[0] === '@')
232
- acc[findAnnotationExpression(elt, pn) ? 0 : 1].push(pn);
233
- return acc;
234
- }, [ [], [] ]);
235
224
  const usingPositionStr = f($path2path(location));
236
225
  const eltRootPath = $path2path(elt.$path);
237
- xprANames.forEach(xprAName => {
238
- //let isSubTreeSpan = true;
226
+
227
+ Object.keys(elt).filter(pn => pn[0] === '@' && findAnnotationExpression(elt, pn)).forEach(xprAName => {
239
228
  transformExpression(elt, xprAName, {
240
229
  ref: (parent, prop, xpr, csnPath) => {
241
230
  let prefixMatch = true;
@@ -245,23 +234,18 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
245
234
  prefixMatch = (xpr[i].id || xpr[i]) === typeRefRootPath[i];
246
235
  }
247
236
  if(prefixMatch && xpr.length > typeRefRootPath.length) {
248
- parent[prop] = [ ...xpr.slice(eltRootPath.length-1)];
237
+ if(xpr.length >= eltRootPath.length)
238
+ parent[prop] = [ ...xpr.slice(eltRootPath.length-1)];
239
+ else
240
+ parent[prop] = [ '$self', ...xpr.slice(typeRefRootPath.length)];
249
241
  }
250
242
  else {
251
243
  error('odata-anno-xpr-ref', csnPath,
252
244
  { anno: xprAName, elemref: xpr.join('.'), name: usingPositionStr, code: typeRefStr });
253
- //isSubTreeSpan = false;
254
245
  }
255
246
  }
256
247
  },
257
248
  }, elt.$path);
258
- // if(!isSubTreeSpan) {
259
- // // remove annotation and sub annotations from cloned element
260
- // delete elt[xprAName];
261
- // nxprANames.filter(an => an.startsWith(`${xprAName}.`)).forEach((nxprAName) => {
262
- // delete elt[nxprAName];
263
- // });
264
- // }
265
249
  });
266
250
  setProp(elt, '$path', location);
267
251
  }, node.$path);
@@ -8,8 +8,9 @@
8
8
 
9
9
  const { setProp, isBetaEnabled } = require('../../base/model');
10
10
  const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
11
- const { getNamespace, copyAnnotations, isBuiltinType, findAnnotationExpression,
11
+ const { getNamespace, copyAnnotations, findAnnotationExpression,
12
12
  forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');
13
+ const { isBuiltinType } = require('../../base/builtins');
13
14
  const { CompilerAssertion } = require('../../base/error');
14
15
  const { cloneCsnNonDict } = require('../../model/cloneCsn');
15
16
 
@@ -78,7 +79,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
78
79
  forEachMember(def, (element, elementName, propertyName, path) => {
79
80
  if (propertyName === 'elements' || propertyName === 'params') {
80
81
  const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
81
- exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, newTypeName, path);
82
+ exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, newTypeName, defName, path);
82
83
  }
83
84
  }, path);
84
85
  }
@@ -105,7 +106,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
105
106
  csn.definitions[defName] = def;
106
107
  const artificialName = `term_${defName.replace(/\./g, '_')}`;//_${paramName}`;
107
108
  const newTypeName = getNewTypeName(undefined, undefined, artificialName, serviceName);
108
- exposeTypeOf(def, false, defName, defName, serviceName, newTypeName, path.concat(['vocabularies', defName]), undefined, true);
109
+ exposeTypeOf(def, false, defName, defName, serviceName, newTypeName, defName, path.concat(['vocabularies', defName]), undefined, true);
109
110
  }
110
111
  }
111
112
  });
@@ -125,13 +126,13 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
125
126
  if (action.returns) {
126
127
  const artificialName = `return_${actionName.replace(/\./g, '_')}`;
127
128
  const newTypeName = getNewTypeName(action.returns, undefined, artificialName, serviceName);
128
- exposeTypeOf(action.returns, false, actionName, defName, serviceName, newTypeName, path.concat(['returns']));
129
+ exposeTypeOf(action.returns, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['returns']));
129
130
  }
130
131
 
131
132
  action.params && Object.entries(action.params).forEach(([paramName, param]) => {
132
133
  const artificialName = `param_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
133
134
  const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);
134
- exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, path.concat(['params', paramName]));
135
+ exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['params', paramName]));
135
136
  });
136
137
  }
137
138
 
@@ -144,7 +145,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
144
145
  * @param {String} serviceName
145
146
  * @param {String} newTypeName
146
147
  */
147
- function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName, isTermDef=false, ignoreInAPI=false) {
148
+ function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, lastNonAnonymousFQDefName, path, parentName, isTermDef=false, ignoreInAPI=false) {
148
149
  const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable();
149
150
  if (isExposable) {
150
151
  // this is the name used to register the new type in csn.definitions
@@ -164,6 +165,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
164
165
  // expose the type as a new one not changing the original definition.
165
166
  if(elements && !!node['@open'] !== !!typeDef['@open'])
166
167
  fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
168
+ lastNonAnonymousFQDefName = fullQualifiedNewTypeName;
167
169
  }
168
170
  // check if that type is already defined
169
171
  let newType = csn.definitions[fullQualifiedNewTypeName];
@@ -197,10 +199,12 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
197
199
  newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
198
200
  if (node.elements && node.elements[elemName].$location)
199
201
  setProp(newElem, '$location', node.elements[elemName].$location);
202
+ if (newElem.$path)
203
+ newElem.$path[1] = lastNonAnonymousFQDefName;
200
204
  defName = typeDef.kind === 'type' ? typeName : defName;
201
205
  {
202
206
  const { isExposable, typeDef, typeName } = exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
203
- getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName, isTermDef, ignoreInAPI);
207
+ getNewTypeName(newElem, elemName, newTypeName, serviceName), lastNonAnonymousFQDefName, path, fullQualifiedNewTypeName, isTermDef, ignoreInAPI);
204
208
  // if the type for the newElem was not exposed it may be a scalar type def from an external service that hasn't
205
209
  // been caught by expandToFinalBaseType() (forODataNew must not modify external imported services)
206
210
  if(!isExposable && isBuiltinType(typeName) && !isBuiltinType((newElem.items?.type || newElem.type))) {
@@ -222,14 +226,23 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
222
226
  }
223
227
  });
224
228
 
225
- // expression annos and their sub annotations are not propagated to type
226
- let [ xprANames, nxprANames ] = Object.keys(typeDef).reduce((acc, pn) => {
227
- if (pn[0] === '@')
228
- acc[findAnnotationExpression(typeDef, pn) ? 0 : 1].push(pn);
229
- return acc;
230
- }, [ [], [] ]);
231
- nxprANames = nxprANames.filter(an => !xprANames.some(ean => an.startsWith(`${ean}.`)));
232
- copyAnnotations(typeDef, newType, false, {}, nxprANames);
229
+ // TODO: remove the this if and the else clause for the V5 release
230
+ if (isBetaEnabled(options, 'v5preview')) {
231
+ // We need this check, as we only need to copy the annotions if the type
232
+ // is user defined structured type outside of the service
233
+ if (!isAnonymous) {
234
+ copyAnnotations(typeDef, newType);
235
+ }
236
+ } else {
237
+ // expression annos and their sub annotations are not propagated to type
238
+ let [ xprANames, nxprANames ] = Object.keys(typeDef).reduce((acc, pn) => {
239
+ if (pn[0] === '@')
240
+ acc[findAnnotationExpression(typeDef, pn) ? 0 : 1].push(pn);
241
+ return acc;
242
+ }, [ [], [] ]);
243
+ nxprANames = nxprANames.filter(an => !xprANames.some(ean => an.startsWith(`${ean}.`)));
244
+ copyAnnotations(typeDef, newType, false, {}, nxprANames);
245
+ }
233
246
 
234
247
  // if the origin type had items, add items to exposed type
235
248
  if(typeDef.kind === 'type') {
@@ -2,9 +2,6 @@
2
2
 
3
3
  'use strict'
4
4
 
5
- // const funkyfuncs = Object.keys(require('../compiler/builtins.js').
6
- // specialFunctions).filter(n => n.length).map(n=>n.toLowerCase());
7
-
8
5
  /**
9
6
  * parseExpr() accepts any JSON object and tries to convert a token stream expression
10
7
  * array into an AST like expression with CDL operator precedence.
@@ -8,10 +8,12 @@ const { setProp } = require('../base/model');
8
8
 
9
9
  const { copyAnnotations, applyTransformations, isDollarSelfOrProjectionOperand } = require('../model/csnUtils');
10
10
  const { getUtils } = require('../model/csnUtils');
11
- const { typeParameters, isBuiltinType } = require('../compiler/builtins');
11
+ const { typeParameters } = require('../compiler/builtins');
12
+ const { isBuiltinType } = require('../base/builtins');
12
13
  const { ModelError, CompilerAssertion} = require('../base/error');
13
14
  const { forEach } = require('../utils/objectUtils');
14
15
  const { cloneCsnNonDict, cloneCsnDict } = require('../model/cloneCsn');
16
+ const { addTenantFieldToArt } = require('./addTenantFields');
15
17
 
16
18
  const RestrictedOperators = ['<', '>', '>=', '<='];
17
19
  const RelationalOperators = ['=', '!=', '<>', 'is' /*, 'like'*/,...RestrictedOperators];
@@ -321,10 +323,10 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
321
323
  * @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
322
324
  * @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
323
325
  * @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
324
- * @param {bool} [refParentIsItems] relative ref has an items root (suspend flattening by caller)
326
+ * @param {bool} [suspend] suspend flattening by caller until association path step
325
327
  * @returns {string[]}
326
328
  */
327
- function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), refParentIsItems=false) {
329
+ function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), suspend=false, suspendPos=0) {
328
330
  // Refs of length 1 cannot contain steps - no need to check
329
331
  if (ref.length < 2 || (scope === '$self' && ref.length === 2)) {
330
332
  return ref;
@@ -350,10 +352,10 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
350
352
  (resolvedLinkTypes.get(links[i])||{})[propName]);
351
353
 
352
354
  let flattenStep = false;
353
- let nextIsItems = !!art('items') || (refParentIsItems && i === 0);
355
+ suspend = !!art('items') || (suspend && i <= suspendPos);
354
356
  for(; i < links.length; i++) {
355
357
 
356
- if (flattenStep && !nextIsItems) {
358
+ if (flattenStep && !suspend) {
357
359
  result[result.length - 1] += pathDelimiter + (ref[i].id ? ref[i].id : ref[i]);
358
360
  // if we had a filter or args, we had an assoc so this step is done
359
361
  // we then keep along the filter/args by updating the id of the current ref
@@ -362,14 +364,14 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
362
364
  result[result.length-1] = ref[i];
363
365
  }
364
366
  // suspend flattening if the next path step has some 'items'
365
- nextIsItems = !!art('items');
367
+ suspend = !!art('items');
366
368
  }
367
369
  else {
368
370
  result.push(ref[i]);
369
371
  }
370
372
  // revoke items suspension for next assoc step
371
- if(nextIsItems && art('target'))
372
- nextIsItems = false;
373
+ if(suspend && art('target'))
374
+ suspend = false;
373
375
 
374
376
  flattenStep = !links[i].art?.kind &&
375
377
  !links[i].art?.SELECT &&
@@ -522,6 +524,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
522
524
  if (!draftAdminDataEntity) {
523
525
  draftAdminDataEntity = createAndAddDraftAdminDataEntity();
524
526
  model.definitions['DRAFT.DraftAdministrativeData'] = draftAdminDataEntity;
527
+ if(options.tenantDiscriminator && options.transformation !== 'odata')
528
+ addTenantFieldToArt(model.definitions['DRAFT.DraftAdministrativeData'], options);
525
529
  }
526
530
 
527
531
  // Create a projection within this service
@@ -600,7 +600,7 @@ function translateAssocsToJoins(model, inputOptions = {})
600
600
  Put all src/tgt path siblings into the EQ term and create the proper path objects
601
601
  with the src/tgt table alias path steps in front.
602
602
  */
603
- let args = compareTenants ? [ addTenantComparison(assoc) ] : [];
603
+ let args = compareTenants && addTenantComparison(assoc) || [];
604
604
  for(let i = 0; i < assoc.$flatSrcFKs.length; i++)
605
605
  {
606
606
  args.push({op: {val: '=' },
@@ -678,7 +678,7 @@ function translateAssocsToJoins(model, inputOptions = {})
678
678
  args[1].path.push({ id: 'tenant' }); // no need for _artifact
679
679
  const comparison = { op: {val: '=' }, args };
680
680
  if (!cond) // for managed assoc
681
- return comparison;
681
+ return [ comparison ];
682
682
  return { op: { val: 'and' }, args: [ comparison, parenthesise(cond) ] };
683
683
  }
684
684
 
@@ -5,6 +5,7 @@ const {
5
5
  } = require('../../model/csnUtils');
6
6
  const { setAnnotationIfNotDefined } = require('./utils');
7
7
  const { CompilerAssertion } = require('../../base/error');
8
+ const { isMagicVariable } = require('../../base/builtins');
8
9
 
9
10
  /**
10
11
  * Set @Core.Computed on the elements of views (and projections) as well
@@ -157,7 +158,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
157
158
  (
158
159
  column.xpr || column.list || column.func || column.val !== undefined || column['#'] !== undefined || column.param ||
159
160
  column.SELECT || column.SET ||
160
- column.ref && [ '$at', '$valid', '$now', '$user', '$tenant', '$session', '$parameters' ].includes(column.ref[0])
161
+ column.ref && (isMagicVariable(column.ref[0]) || column.ref[0] === '$parameters')
161
162
  );
162
163
  }
163
164
 
@@ -8,8 +8,8 @@ const {
8
8
  getUtils,
9
9
  applyTransformations,
10
10
  implicitAs,
11
- isBuiltinType,
12
11
  } = require('../../model/csnUtils');
12
+ const { isBuiltinType } = require('../../base/builtins');
13
13
  const {
14
14
  forEachValue, forEach,
15
15
  } = require('../../utils/objectUtils');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "4.7.6",
3
+ "version": "4.8.0",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -28,7 +28,7 @@
28
28
  "gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
29
29
  "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
30
30
  "coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
31
- "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint .",
31
+ "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
32
32
  "tslint": "tsc --pretty -p .",
33
33
  "updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
34
34
  "updateTocs": "node scripts/update-toc.js",
@@ -41,8 +41,12 @@ Usually mentions the severity.
41
41
 
42
42
  ## Example
43
43
 
44
+ Erroneous code example:
45
+
46
+ ```
44
47
  Code which also lead to the current message,
45
48
  with an explanation why it is problematic.
49
+ ```
46
50
 
47
51
  ## How to Fix
48
52
 
@@ -23,7 +23,7 @@ annotate FooBar with @Anno: 'Foo';
23
23
  using from './Base';
24
24
  annotate FooBar with @Anno: 'Bar';
25
25
 
26
- // (4) All.cds: Combine all files
26
+ // (4) All.cds: Combine all files
27
27
  using from './FooAnnotate';
28
28
  using from './BarAnnotate';
29
29
  ```
@@ -18,7 +18,7 @@ view ViewFoo as select from Foo {
18
18
  1+1 as calculatedField @(anno)
19
19
  };
20
20
  entity Bar {
21
- // `e` has no proper type but has the annotation `@anno`.
21
+ // `e` has no proper type but has the annotation `@anno`.
22
22
  e : ViewFoo:calculatedField;
23
23
  };
24
24
  ```
@@ -31,7 +31,7 @@ entity ns.Base {
31
31
  to_second : Composition of many ns.second.Foo;
32
32
  }
33
33
  service ns.MyService {
34
- // (4)
34
+ // (4)
35
35
  entity BaseView as projection on ns.Base;
36
36
  };
37
37
  ```
@@ -9,30 +9,17 @@ They form a cyclic connection through their dependencies
9
9
 
10
10
  ## Example
11
11
 
12
- Erroneous code example with a single CDL file:
13
-
14
- ```cds
15
- entity FooBar { }
16
-
17
- extend FooBar { foo : Integer; }
18
- extend FooBar { bar : Integer; }
19
- ```
20
-
21
- Due to multiple extensions in the example above, the order of `foo` and `bar`
22
- inside `FooBar` may not be stable. You therefore can’t depend on it.
23
-
24
- It's also possible to trigger this warning with multiple files.
25
- Look at the following example:
12
+ Erroneous code example with multiple CDL files:
26
13
 
27
14
  ```cds
28
15
  // (1) Definition.cds
29
16
  using from './Extension.cds';
30
17
  entity FooBar { };
31
- extend FooBar { foo: Integer; };
18
+ extend FooBar { foo: Integer; }; // ❌
32
19
 
33
20
  // (2) Extension.cds
34
21
  using from './Definition.cds';
35
- extend FooBar { bar: Integer; }
22
+ extend FooBar { bar: Integer; }; // ❌
36
23
  ```
37
24
 
38
25
  Here we have a cyclic dependency between (1) and (2). Together they form one
@@ -23,7 +23,7 @@ extend FooBar { foo : Integer; }
23
23
  using from './Base';
24
24
  extend FooBar { bar : Integer; }
25
25
 
26
- // (4) All.cds: Combine all files
26
+ // (4) All.cds: Combine all files
27
27
  using from './FooExtend';
28
28
  using from './BarExtend';
29
29
  ```
@@ -12,6 +12,7 @@
12
12
  "redirected-to-unrelated",
13
13
  "rewrite-not-supported",
14
14
  "syntax-expecting-unsigned-int",
15
+ "type-missing-enum-value",
15
16
  "wildcard-excluding-one"
16
17
  ]
17
18
  }
@@ -26,7 +26,7 @@ view View as select from
26
26
  Target,
27
27
  Target as Duplicate
28
28
  {
29
- // This redirection can’t be resolved:
29
+ // This redirection can’t be resolved:
30
30
  Main.toTarget : redirected to View
31
31
  };
32
32
  ```
@@ -21,7 +21,7 @@ entity Secondary {
21
21
  entity CrossJoin as SELECT from Main, Secondary;
22
22
  entity RedirectToComplex as projection on Main {
23
23
  id,
24
- toMain: redirected to CrossJoin,
24
+ toMain: redirected to CrossJoin, // ❌
25
25
  };
26
26
  ```
27
27
 
@@ -21,7 +21,7 @@ entity Secondary {
21
21
  }
22
22
  entity InvalidRedirect as projection on Main {
23
23
  id,
24
- // Invalid redirection
24
+ // Invalid redirection
25
25
  toMain: redirected to Secondary,
26
26
  };
27
27
  ```
@@ -29,7 +29,7 @@ entity Secondary {
29
29
 
30
30
  entity View as select from Base {
31
31
  id,
32
- primary.secondary // Error: The ON condition isn’t rewritten here
32
+ primary.secondary // The ON condition isn’t rewritten here
33
33
  };
34
34
  ```
35
35
 
@@ -17,8 +17,8 @@ Erroneous code example:
17
17
 
18
18
  <!-- cds-mode: ignore -->
19
19
  ```cds
20
- type LengthIsUnsafe : String(9007199254740992);
21
- type NotAnInteger : String(42.1);
20
+ type LengthIsUnsafe : String(9007199254740992); // ❌
21
+ type NotAnInteger : String(42.1); // ❌
22
22
  ```
23
23
 
24
24
  In the erroneous example, the string length for the type `LengthIsUnsafe` is
@@ -0,0 +1,59 @@
1
+ # type-missing-enum-value
2
+
3
+ An enum definition is missing explicit values for one or more of its entries.
4
+
5
+ Enum definitions that aren't based on string-types do not get implicit values.
6
+ They have therefore to be defined explicitly in the model.
7
+
8
+ The message’s severity is `Warning` and is raised by the compiler. You need
9
+ to adapt your model to fix the warning.
10
+
11
+ ## Example
12
+
13
+ Erroneous code example:
14
+
15
+ ```cds
16
+ entity Books {
17
+ // …
18
+ category: Integer enum {
19
+ Fiction; // ❌
20
+ Action; // ❌
21
+ // …
22
+ } default #Action;
23
+ };
24
+ ```
25
+
26
+ Both entries `#Fiction` and `#Action` of the enum `category` are missing an
27
+ explicit value. Because the base type `Integer` is not a string, no implicit
28
+ values are defined for them.
29
+
30
+ ## How to Fix
31
+
32
+ Explicitly assign a value or change the type to a string if the values are not
33
+ important in your model. The erroneous example above can be changed to:
34
+
35
+ ```cds
36
+ entity Books {
37
+ // …
38
+ category: Integer enum {
39
+ Fiction = 1;
40
+ Action = 2;
41
+ // …
42
+ } default #Action;
43
+ };
44
+ ```
45
+
46
+ ## Background
47
+
48
+ Many languages support implicit values for integer-like enums. However,
49
+ CAP CDS does not have this feature, because otherwise, if values are persisted,
50
+ adding a new entry in-between existing ones would lead to issues during
51
+ deserialization later on.
52
+
53
+ Assume that CAP would assign implicit values for integer enums. If a new value
54
+ were to be added between `Fiction` and `Action` in the erroneous example above,
55
+ then the generated SQL statement for entity `Books` would change:
56
+ Instead of default value `2`, value `3` would be persisted. Without data
57
+ migration, existing action books would have changed their category.
58
+
59
+ To avoid this scenario, always add explicit values to enums.
@@ -17,7 +17,7 @@ entity Book {
17
17
  };
18
18
  entity IsbnBook as projection on Book {
19
19
  *,
20
- isbn as id,
20
+ isbn as id, // ❌
21
21
  };
22
22
  ```
23
23