@sap/cds-compiler 3.6.0 → 3.7.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 (70) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +9 -5
  4. package/doc/CHANGELOG_BETA.md +20 -2
  5. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  6. package/lib/api/main.js +2 -1
  7. package/lib/api/options.js +3 -2
  8. package/lib/base/dictionaries.js +10 -0
  9. package/lib/base/message-registry.js +56 -12
  10. package/lib/base/messages.js +39 -20
  11. package/lib/base/model.js +1 -0
  12. package/lib/base/shuffle.js +2 -1
  13. package/lib/checks/elements.js +29 -1
  14. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +9 -5
  15. package/lib/checks/nonexpandableStructured.js +1 -1
  16. package/lib/checks/onConditions.js +8 -5
  17. package/lib/checks/types.js +6 -1
  18. package/lib/checks/validator.js +7 -3
  19. package/lib/compiler/assert-consistency.js +20 -23
  20. package/lib/compiler/base.js +1 -2
  21. package/lib/compiler/builtins.js +2 -2
  22. package/lib/compiler/checks.js +237 -242
  23. package/lib/compiler/define.js +63 -75
  24. package/lib/compiler/extend.js +325 -22
  25. package/lib/compiler/finalize-parse-cdl.js +1 -55
  26. package/lib/compiler/kick-start.js +6 -7
  27. package/lib/compiler/populate.js +284 -288
  28. package/lib/compiler/propagator.js +15 -13
  29. package/lib/compiler/resolve.js +136 -306
  30. package/lib/compiler/shared.js +42 -44
  31. package/lib/compiler/tweak-assocs.js +29 -27
  32. package/lib/compiler/utils.js +29 -3
  33. package/lib/edm/annotations/genericTranslation.js +7 -13
  34. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  35. package/lib/edm/csn2edm.js +0 -4
  36. package/lib/edm/edm.js +6 -4
  37. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  38. package/lib/edm/edmPreprocessor.js +1 -5
  39. package/lib/gen/Dictionary.json +34 -2
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +2429 -2401
  43. package/lib/inspect/inspectPropagation.js +2 -0
  44. package/lib/json/from-csn.js +87 -41
  45. package/lib/json/to-csn.js +47 -16
  46. package/lib/language/errorStrategy.js +1 -0
  47. package/lib/language/genericAntlrParser.js +109 -28
  48. package/lib/language/language.g4 +20 -4
  49. package/lib/model/csnRefs.js +30 -2
  50. package/lib/model/csnUtils.js +1 -0
  51. package/lib/model/revealInternalProperties.js +1 -2
  52. package/lib/modelCompare/compare.js +2 -1
  53. package/lib/optionProcessor.js +3 -0
  54. package/lib/render/manageConstraints.js +5 -2
  55. package/lib/render/toCdl.js +20 -7
  56. package/lib/render/toHdbcds.js +2 -8
  57. package/lib/render/toSql.js +6 -5
  58. package/lib/render/utils/common.js +9 -5
  59. package/lib/transform/db/assertUnique.js +2 -1
  60. package/lib/transform/db/expansion.js +2 -0
  61. package/lib/transform/db/flattening.js +37 -36
  62. package/lib/transform/db/rewriteCalculatedElements.js +559 -0
  63. package/lib/transform/db/transformExists.js +15 -6
  64. package/lib/transform/db/views.js +40 -37
  65. package/lib/transform/forRelationalDB.js +44 -30
  66. package/lib/transform/odata/typesExposure.js +50 -15
  67. package/lib/transform/parseExpr.js +14 -8
  68. package/lib/transform/transformUtilsNew.js +6 -5
  69. package/lib/transform/translateAssocsToJoins.js +49 -33
  70. package/package.json +1 -1
@@ -4,7 +4,6 @@
4
4
  'use strict';
5
5
 
6
6
  const { searchName } = require('../base/messages');
7
- const { dictAddArray } = require('../base/dictionaries');
8
7
  const { isDeprecatedEnabled } = require('../base/model');
9
8
 
10
9
  const {
@@ -184,7 +183,6 @@ function fns( model ) {
184
183
  resolveTypeArgumentsUnchecked,
185
184
  resolvePath,
186
185
  checkAnnotate,
187
- copyAnnotationsForExtensions,
188
186
  attachAndEmitValidNames,
189
187
  } );
190
188
  return;
@@ -253,16 +251,12 @@ function fns( model ) {
253
251
  }
254
252
 
255
253
  function checkSourceRef( art, path ) { // for FROM
256
- if (art.kind === 'entity' )
257
- return false;
258
- if (art.kind !== 'element')
259
- return true;
254
+ if (!art._main)
255
+ return (art.kind !== 'entity');
260
256
  const elem = path.find( item => item._artifact._main )._artifact;
261
- // TODO: better error location if error for main
262
- if (elem._main.kind !== 'entity' )
263
- return true; // elem not starting at entity
264
- VolatileFns.environment( art ); // sets _effectiveType on art
265
- return !(art._effectiveType || art).target;
257
+ // at least the last main definition should be an entity
258
+ // an additional check for target would need effectiveType()
259
+ return (elem && elem._main.kind !== 'entity');
266
260
  }
267
261
 
268
262
  // Return absolute name for unchecked path `ref`. We first try searching for
@@ -313,7 +307,6 @@ function fns( model ) {
313
307
  // incomplete type AST or empty env (already reported)
314
308
  return setArtifactLink( ref, undefined );
315
309
  }
316
- setArtifactLink( ref, 0 ); // avoid cycles for type T: association to T.m;
317
310
 
318
311
  let spec = specExpected[expected];
319
312
  const { path } = ref;
@@ -324,10 +317,12 @@ function fns( model ) {
324
317
 
325
318
  if (ref.scope === 'param') {
326
319
  if (!spec.escape) {
327
- error( 'ref-unexpected-scope', [ ref.location, user ], {},
328
- 'Unexpected parameter reference' );
320
+ error( 'ref-unexpected-scope', [ ref.location, user ], { '#': 'std' } );
329
321
  return setArtifactLink( ref, null );
330
322
  }
323
+ if (user.$syntax === 'calc')
324
+ error('ref-unexpected-scope', [ ref.location, user ], { '#': 'calc' } );
325
+
331
326
  spec = specExpected[spec.escape];
332
327
  // In queries and query entities, the first lexical search environment
333
328
  // are the parameters, otherwise the block. It is currently ensured that
@@ -363,8 +358,12 @@ function fns( model ) {
363
358
  return setArtifactLink( ref, art );
364
359
  }
365
360
  else if (!spec.envFn && user._pathHead) {
366
- if (art.kind === '$self')
367
- rejectBareSelf( spec, path, user, extDict );
361
+ if (art.kind === '$self') {
362
+ const headEnv = VolatileFns.environment( user._pathHead ) &&
363
+ user._pathHead._origin &&
364
+ VolatileFns.environment( user._pathHead._origin );
365
+ rejectBareSelf( spec, path, user, headEnv );
366
+ }
368
367
  }
369
368
  else if (art.kind === 'using') {
370
369
  const def = model.definitions[art.name.absolute];
@@ -464,13 +463,13 @@ function fns( model ) {
464
463
  spec.noDep === 'only-entity' && art.kind !== 'entity')) {
465
464
  const { location } = ref; // || combinedLocation( head, path[tail.length] );
466
465
  // TODO: location of last path item if not main artifact
467
- if (!art._main || spec.assoc !== 'from') {
468
- dependsOn( user, art, location );
469
- }
470
- else {
466
+ if (spec.assoc === 'from' && art._main) {
471
467
  dependsOn( user, art._main, location );
472
468
  VolatileFns.environment( art, location, user );
473
- // Without on-demand resolve, we can simply signal 'undefined "x"'
469
+ }
470
+ else if (art.kind !== 'select') { // no real dependency to bare $self
471
+ dependsOn( user, art, location );
472
+ // Without on-demand resolve, we can simply signal 'undefined "x"'
474
473
  // instead of 'illegal cycle' in the following case:
475
474
  // element elem: type of elem.x;
476
475
  }
@@ -772,12 +771,13 @@ function fns( model ) {
772
771
 
773
772
  const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : VolatileFns.environment;
774
773
  const env = fn( art, item.location, user, spec.assoc );
775
- const sub = setArtifactLink( item, env && env[item.id] );
774
+ const sub = setArtifactLink( item, env?.[item.id] );
776
775
 
777
776
  if (!sub) {
778
777
  // element was not found in environment
779
- if (sub === 0)
780
- return 0;
778
+
779
+ // TODO (done?): if `env` was 0, we might set a dependency to induce an
780
+ // illegal-cycle error instead of reporting via `errorNotFound`.
781
781
  if (art.$uncheckedElements) { // magic variable / replacement variable
782
782
  signalNotFound( 'ref-unknown-var', [ item.location, user ], [ env ],
783
783
  { id: pathName( path ) } );
@@ -971,26 +971,24 @@ function fns( model ) {
971
971
  info( 'anno-builtin', [ construct.name.location, construct ], {},
972
972
  'Builtin types should not be annotated. Use custom type instead' );
973
973
  }
974
- else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
975
- art.kind !== 'function' ) {
976
- // `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
977
- // for non-actions. We can't only check for !art.returns, because `action A();` is valid.
978
- // `art._block` ensures that `art` is a defined def.
979
- warning('anno-unexpected-returns', [ construct.name.location, construct ],
980
- { keyword: 'returns', meta: art.kind }, 'Unexpected $(KEYWORD) for $(META)');
981
- }
982
- }
983
-
984
- // Copy annotations from `ext` to `art`, overwriting inferred ones.
985
- // TODO: move to extend.js if not used anymore in define.js
986
- function copyAnnotationsForExtensions( ext, art ) {
987
- for (const annoProp in ext) {
988
- if (annoProp.charAt(0) === '@' || annoProp === 'doc') {
989
- const extAnno = ext[annoProp];
990
- if (art[annoProp]?.$inferred)
991
- art[annoProp] = extAnno; // overwrite $inferred annos
992
- else
993
- dictAddArray( art, annoProp, extAnno );
974
+ // --> without art._block, art not found
975
+ else if (construct.kind === 'annotate' && art._block?.$frontend === 'cdl') {
976
+ if (construct.$syntax === 'returns' && art.kind !== 'action' && art.kind !== 'function' ) {
977
+ // `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
978
+ // for non-actions. We can't only check for !art.returns, because `action A();` is valid.
979
+ // `art._block` ensures that `art` is a defined def.
980
+ warning('ext-unexpected-returns', [ construct.name.location, construct ],
981
+ { keyword: 'returns', meta: art.kind }, 'Unexpected $(KEYWORD) for $(META)');
982
+ }
983
+ else if (construct.$syntax !== 'returns' &&
984
+ (art.kind === 'action' || art.kind === 'function') && construct.elements) {
985
+ warning('ext-expected-returns', [ construct.name.location, construct ], {
986
+ '#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
987
+ }, {
988
+ std: 'Expected $(CODE)', // unused variant
989
+ action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
990
+ function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
991
+ });
994
992
  }
995
993
  }
996
994
  }
@@ -3,7 +3,6 @@
3
3
  'use strict';
4
4
 
5
5
  const {
6
- forEachDefinition,
7
6
  forEachGeneric,
8
7
  forEachInOrder,
9
8
  } = require('../base/model');
@@ -31,19 +30,13 @@ function tweakAssocs( model ) {
31
30
  } = model.$messageFunctions;
32
31
  const {
33
32
  effectiveType,
34
- directType,
33
+ getOrigin,
35
34
  resolveExpr,
36
35
  } = model.$functions;
37
36
  const { environment } = model.$volatileFunctions;
38
37
 
39
38
  // Phase 5: rewrite associations
40
- forEachDefinition( model, rewriteSimple );
41
- // TODO: sequence not good enough with derived type of structure with
42
- // includes: first "direct" structures, then _entities, then the rest.
43
- // v2: We might run a silent cycle detection earlier, then we could use the
44
- // SCC number (_scc.lowlink) to sort.
45
- model._entities.forEach( rewriteView );
46
- model._entities.forEach( rewriteViewCheck );
39
+ model._entities.forEach( rewriteArtifact );
47
40
  // Think hard whether an on condition rewrite can lead to a new cylcic
48
41
  // dependency. If so, we need other messages anyway. TODO: probably do
49
42
  // another cyclic check with testMode.js
@@ -55,33 +48,41 @@ function tweakAssocs( model ) {
55
48
  //--------------------------------------------------------------------------
56
49
  // Only top-level queries and sub queries in FROM
57
50
 
58
- function rewriteSimple( art ) {
51
+ function rewriteArtifact( art ) {
59
52
  // return;
60
- if (!art.includes && !art.query) {
53
+ if (!art.query) {
61
54
  // console.log(message( null, art.location, art, {target:art._target},
62
55
  // 'Info','RAS').toString())
63
56
  rewriteAssociation( art );
64
57
  forEachGeneric( art, 'elements', rewriteAssociation );
65
58
  }
59
+ else {
60
+ traverseQueryExtra( art, ( query ) => {
61
+ forEachGeneric( query, 'elements', rewriteAssociation );
62
+ } );
63
+ }
66
64
  if (art._service)
67
65
  forEachGeneric( art, 'elements', excludeAssociation );
68
- }
69
66
 
70
- function rewriteView( view ) {
71
- traverseQueryExtra( view, ( query ) => {
72
- forEachGeneric( query, 'elements', rewriteAssociation );
67
+ traverseQueryPost( art.query, false, ( query ) => {
68
+ forEachGeneric( query, 'elements', rewriteAssociationCheck );
73
69
  } );
74
- if (view.includes) // entities with structure includes:
75
- forEachGeneric( view, 'elements', rewriteAssociation );
76
70
  }
77
71
 
72
+ // function rewriteView( view ) {
73
+ // // TODO: we could sort according to the $effectiveSeqNo instead
74
+ // // (and then remove traverseQueryExtra)
75
+ // if (view.includes) // entities with structure includes:
76
+ // forEachGeneric( view, 'elements', rewriteAssociation );
77
+ // }
78
+
78
79
  // Check explicit ON / keys with REDIRECTED TO
79
80
  // TODO: run on all queries, but this is potentially incompatible
80
- function rewriteViewCheck( view ) {
81
- traverseQueryPost( view.query, false, ( query ) => {
82
- forEachGeneric( query, 'elements', rewriteAssociationCheck );
83
- } );
84
- }
81
+ // function rewriteViewCheck( view ) {
82
+ // traverseQueryPost( view.query, false, ( query ) => {
83
+ // forEachGeneric( query, 'elements', rewriteAssociationCheck );
84
+ // } );
85
+ // }
85
86
 
86
87
  function excludeAssociation( elem ) {
87
88
  const target = elem.target && elem.target._artifact;
@@ -112,7 +113,7 @@ function tweakAssocs( model ) {
112
113
  if (!elem.target)
113
114
  return;
114
115
  if (elem.on && !elem.on.$inferred) {
115
- const assoc = directType( elem );
116
+ const assoc = getOrigin( elem );
116
117
  if (assoc && assoc.foreignKeys) {
117
118
  error( 'rewrite-key-for-unmanaged', [ elem.on.location, elem ],
118
119
  { keyword: 'on', art: assocWithExplicitSpec( assoc ) },
@@ -121,7 +122,7 @@ function tweakAssocs( model ) {
121
122
  }
122
123
  }
123
124
  else if (elem.foreignKeys && !inferredForeignKeys( elem.foreignKeys )) {
124
- const assoc = directType( elem );
125
+ const assoc = getOrigin( elem );
125
126
  if (assoc && assoc.on) {
126
127
  error( 'rewrite-on-for-managed',
127
128
  [ elem.foreignKeys[$location] || dictLocation( elem.foreignKeys ), elem ],
@@ -195,7 +196,7 @@ function tweakAssocs( model ) {
195
196
  function assocWithExplicitSpec( assoc ) {
196
197
  while (assoc.foreignKeys && inferredForeignKeys( assoc.foreignKeys, 'keys') ||
197
198
  assoc.on && assoc.on.$inferred)
198
- assoc = directType( assoc );
199
+ assoc = getOrigin( assoc );
199
200
  return assoc;
200
201
  }
201
202
 
@@ -211,6 +212,7 @@ function tweakAssocs( model ) {
211
212
  // With cyclic dependencies on select items, testing for the _effectiveType to
212
213
  // be 0 (test above) is not enough if we we have an explicit redirection
213
214
  // target -> avoid infloop ourselves with _status.
215
+ // TODO: this should be good now
214
216
  const chain = [];
215
217
  while (!elem.on && !elem.foreignKeys) {
216
218
  chain.push( elem );
@@ -220,7 +222,7 @@ function tweakAssocs( model ) {
220
222
  return;
221
223
  }
222
224
  setLink( elem, '_status', 'rewrite' );
223
- elem = directType( elem );
225
+ elem = getOrigin( elem );
224
226
  if (!elem || elem.builtin) // safety
225
227
  return;
226
228
  }
@@ -236,7 +238,7 @@ function tweakAssocs( model ) {
236
238
  }
237
239
 
238
240
  function originTarget( elem ) {
239
- const assoc = !elem.expand && directType( elem );
241
+ const assoc = !elem.expand && getOrigin( elem );
240
242
  const ftype = assoc && effectiveType( assoc );
241
243
  return ftype && ftype.target && ftype.target._artifact;
242
244
  }
@@ -10,7 +10,7 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- const { dictAdd, pushToDict } = require('../base/dictionaries');
13
+ const { dictAdd, pushToDict, dictFirst } = require('../base/dictionaries');
14
14
  const { kindProperties } = require('./base');
15
15
 
16
16
  // for links, i.e., properties starting with an underscore '_':
@@ -71,10 +71,11 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
71
71
 
72
72
  // The link (_artifact,_effectiveType,...) usually has the artifact as value.
73
73
  // Falsy values are:
74
- // - undefined: not computed yet, parse error, no ref
75
- // - null: no valid reference, param:true if that is not allowed
74
+ // - undefined: not computed yet, parse error (TODO: null), no ref
75
+ // - null: ref to unknown, param:true if that is not allowed (TODO: false)
76
76
  // - false (only complete ref): multiple definitions, rejected
77
77
  // - 0 (for _effectiveType only): circular reference
78
+ // - '' (for _origin only): no origin provided
78
79
  function setLink( obj, prop, value ) {
79
80
  Object.defineProperty( obj, prop, { value, configurable: true, writable: true } );
80
81
  return value;
@@ -105,6 +106,20 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
105
106
  return elem;
106
107
  }
107
108
 
109
+ function proxyCopyMembers( art, dictProp, originDict, location, kind ) {
110
+ art[dictProp] = Object.create( null );
111
+ for (const name in originDict) {
112
+ const origin = originDict[name];
113
+ const member = linkToOrigin( origin, name, art, dictProp,
114
+ location || origin.location, true );
115
+ member.$inferred = 'expanded';
116
+ if (kind)
117
+ member.kind = kind;
118
+ if (kind && origin.masked) // TODO: remove!
119
+ member.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
120
+ }
121
+ }
122
+
108
123
  /**
109
124
  * Set the member `elem` to have a _parent link to `parent` and a corresponding
110
125
  * _main link. Also set the member's name accordingly, where argument `name`
@@ -370,6 +385,15 @@ function traverseQueryExtra( main, callback ) {
370
385
  }
371
386
  }
372
387
 
388
+ // Returns what was available at view._from[0] before:
389
+ // (think first whether to really use this function)
390
+ function viewFromPrimary( view ) {
391
+ let query = view.$queries?.[0];
392
+ while (query?._origin?.kind === 'select') // sub query in from
393
+ query = query._origin;
394
+ return dictFirst( query?.$tableAliases );
395
+ }
396
+
373
397
  // About Helper property $expand for faster the XSN-to-CSN transformation
374
398
  // - null/undefined: artifact, member, items does not contain expanded members
375
399
  // - 'origin': all expanded (sub) elements have no new target/on and no new annotations
@@ -428,6 +452,7 @@ module.exports = {
428
452
  setLink,
429
453
  setArtifactLink,
430
454
  linkToOrigin,
455
+ proxyCopyMembers,
431
456
  dependsOn,
432
457
  dependsOnSilent,
433
458
  setMemberParent,
@@ -441,6 +466,7 @@ module.exports = {
441
466
  targetMaxNotOne,
442
467
  traverseQueryPost,
443
468
  traverseQueryExtra,
469
+ viewFromPrimary,
444
470
  setExpandStatus,
445
471
  setExpandStatusAnnotate,
446
472
  isDirectComposition,
@@ -1179,21 +1179,15 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName, Edm = undefine
1179
1179
  }
1180
1180
  }
1181
1181
  else if (typeof value === 'number') {
1182
- if (isComplexType(resolvedType)) {
1182
+ if (isComplexType(resolvedType) ||
1183
+ resolvedType === 'Edm.PropertyPath' ||
1184
+ resolvedType === 'Edm.Boolean') {
1183
1185
  message('odata-anno-value', msg.location,
1184
- { anno: msg.anno(), value, type: resolvedType });
1186
+ { anno: msg.anno(), value, type: resolvedType });
1185
1187
  }
1186
1188
  else if (resolvedType === 'Edm.String') {
1187
1189
  typeName = 'String';
1188
1190
  }
1189
- else if (resolvedType === 'Edm.PropertyPath') {
1190
- message('odata-anno-value', msg.location,
1191
- { anno: msg.anno(), value, type: resolvedType });
1192
- }
1193
- else if (resolvedType === 'Edm.Boolean') {
1194
- message('odata-anno-value', msg.location,
1195
- { anno: msg.anno(), value, type: resolvedType });
1196
- }
1197
1191
  else if (resolvedType === 'Edm.Decimal') {
1198
1192
  typeName = 'Decimal';
1199
1193
  }
@@ -1204,18 +1198,18 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName, Edm = undefine
1204
1198
  //typeName = Number.isInteger(val) ? 'Int' : 'Float';
1205
1199
  if(Number.isInteger(value)) {
1206
1200
  typeName = 'Int';
1207
- if(resolvedType == undefined || resolvedType === 'Edm.PrimitiveType' || !resolvedType.startsWith('Edm.'))
1201
+ if(resolvedType == null || resolvedType === 'Edm.PrimitiveType' || !resolvedType.startsWith('Edm.'))
1208
1202
  resolvedType = 'Edm.Int64';
1209
1203
  }
1210
1204
  else {
1211
1205
  typeName = 'Float';
1212
- if(resolvedType == undefined || resolvedType === 'Edm.PrimitiveType'|| !resolvedType.startsWith('Edm.'))
1206
+ if(resolvedType == null || resolvedType === 'Edm.PrimitiveType'|| !resolvedType.startsWith('Edm.'))
1213
1207
  resolvedType = 'Edm.Double';
1214
1208
  }
1215
1209
  }
1216
1210
  }
1217
1211
  else if (value === null)
1218
- if((resolvedType == null || resolvedType == 'Edm.PrimitiveType') && typeName === 'String') {
1212
+ if((resolvedType == null || resolvedType === 'Edm.PrimitiveType') && typeName === 'String') {
1219
1213
  resolvedType = 'Edm.String';
1220
1214
  }
1221
1215
  else {
@@ -63,6 +63,9 @@ function preprocessAnnotations(csn, serviceName, options) {
63
63
  artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
64
64
  handleAnnotations(artifactName, elementName, element, [ ...location, 'elements', elementName ]);
65
65
  });
66
+ artifact.params && Object.entries(artifact.params).forEach(([paramName, param]) => {
67
+ handleAnnotations(artifactName, paramName, param, [ ...location, 'actions', artifactName, 'params', paramName ]);
68
+ });
66
69
  forEachGeneric(artifact, 'actions', (action, actionName) => {
67
70
  action.params && Object.entries(action.params).forEach(([paramName, param]) => {
68
71
  handleAnnotations(actionName, paramName, param, [ ...location, 'actions', actionName, 'params', paramName ]);
@@ -518,10 +518,6 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
518
518
  const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
519
519
  const loc = ['definitions', structuredTypeCsn.name];
520
520
 
521
- if(properties.length === 0) {
522
- warning(null, loc, { name: structuredTypeCsn.name },
523
- 'EDM ComplexType $(NAME) has no properties');
524
- }
525
521
  if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
526
522
  message('odata-spec-violation-id', loc, { id: attributes.Name });
527
523
 
package/lib/edm/edm.js CHANGED
@@ -749,7 +749,7 @@ function getEdm(options, messageFunctions) {
749
749
  this._edmAttributes && Object.entries(this._edmAttributes).forEach(([p, v]) => {
750
750
  if (p !== 'Name' && p !== this._typeName
751
751
  // remove this line if Nullable=true becomes default
752
- && !(p === 'Nullable' && v == false))
752
+ && !(p === 'Nullable' && !v))
753
753
  {
754
754
  json[p[0] === '@' ? p : '$' + p] = v;
755
755
  }
@@ -763,7 +763,7 @@ function getEdm(options, messageFunctions) {
763
763
  }
764
764
  }
765
765
 
766
- class ComplexType extends TypeBase {
766
+ class ComplexType extends TypeBase {
767
767
  constructor(v, details, csn) {
768
768
  super(v, details, csn);
769
769
  if(this.v4 && !!csn['@open'] && isBetaEnabled(options, 'odataOpenType')) {
@@ -979,6 +979,7 @@ function getEdm(options, messageFunctions) {
979
979
  // TIPHANACDS-4180
980
980
  if(this.v2)
981
981
  {
982
+ // eslint-disable-next-line sonarjs/no-redundant-boolean
982
983
  if(csn['@odata.etag'] == true || csn['@cds.etag'] == true)
983
984
  this._edmAttributes.ConcurrencyMode='Fixed'
984
985
 
@@ -1101,8 +1102,8 @@ function getEdm(options, messageFunctions) {
1101
1102
  delete this._edmAttributes.Nullable;
1102
1103
  }
1103
1104
  // we have exactly one selfReference or the default partner
1104
- let partner =
1105
- !csn.$noPartner ?
1105
+ let partner =
1106
+ !csn.$noPartner ?
1106
1107
  csn._selfReferences.length === 1
1107
1108
  ? csn._selfReferences[0]
1108
1109
  : csn._constraints._partnerCsn
@@ -1121,6 +1122,7 @@ function getEdm(options, messageFunctions) {
1121
1122
  See csn2edm.createParmeterizedEntityTypeAndSet() for details
1122
1123
  2) ContainsTarget stems from the @odata.contained annotation
1123
1124
  */
1125
+ // eslint-disable-next-line sonarjs/no-redundant-boolean
1124
1126
  if(csn['@odata.contained'] == true || csn.containsTarget) {
1125
1127
  this._edmAttributes.ContainsTarget = true;
1126
1128
  }
@@ -70,6 +70,7 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
70
70
  // for @[odata|cds].etag annotations...
71
71
  if(options.isV4())
72
72
  {
73
+ // eslint-disable-next-line sonarjs/no-redundant-boolean
73
74
  if(element['@odata.etag'] == true || element['@cds.etag'] == true) {
74
75
  // don't put element name into collection as per advice from Ralf Handl, as
75
76
  // no runtime is interested in the property itself, it is sufficient to mark
@@ -100,10 +100,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
100
100
 
101
101
  /*
102
102
  Enrich the CSN by de-anonymizing and exposing types that are required to make the service self contained.
103
- Type exposure will add additional schema contexts and group the exposed types in these contexts.
104
- Contexts either represent another service (if the type to be exposed resides in that
105
- service), the namespace (including (sub-)contexts) or as last resort (if the type name
106
- has no prefix path) a 'root' namespace.
107
103
  */
108
104
  const schemas = typesExposure(csn, whatsMyServiceRootName, requestedServiceNames,
109
105
  fallBackSchemaName, options, csnUtils, { error });
@@ -1933,7 +1929,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1933
1929
  const localRestrictions = container[NavResAnno] ?
1934
1930
  cloneAnnotationValue(container[NavResAnno]) : []
1935
1931
 
1936
- // prefix the existing navigation property restritictions on the container
1932
+ // prefix the existing navigation property restrictions on the container
1937
1933
  if(prefix.length) {
1938
1934
  localRestrictions.forEach(npe => {
1939
1935
  if(npe.NavigationProperty &&
@@ -512,6 +512,14 @@
512
512
  ],
513
513
  "$experimental": true
514
514
  },
515
+ "Common.IsTimezone": {
516
+ "Type": "Core.Tag",
517
+ "AppliesTo": [
518
+ "Property",
519
+ "Parameter"
520
+ ],
521
+ "$experimental": true
522
+ },
515
523
  "Common.IsDigitSequence": {
516
524
  "Type": "Core.Tag",
517
525
  "AppliesTo": [
@@ -1376,6 +1384,16 @@
1376
1384
  "Function"
1377
1385
  ]
1378
1386
  },
1387
+ "Core.RequiresExplicitBinding": {
1388
+ "Type": "Core.Tag",
1389
+ "AppliesTo": [
1390
+ "Action",
1391
+ "Function"
1392
+ ]
1393
+ },
1394
+ "Core.ExplicitOperationBindings": {
1395
+ "Type": "Collection(Core.QualifiedBoundOperationName)"
1396
+ },
1379
1397
  "Core.SymbolicName": {
1380
1398
  "Type": "Core.SimpleIdentifier"
1381
1399
  },
@@ -1457,30 +1475,35 @@
1457
1475
  "Measures.ISOCurrency": {
1458
1476
  "Type": "Edm.String",
1459
1477
  "AppliesTo": [
1478
+ "Parameter",
1460
1479
  "Property"
1461
1480
  ]
1462
1481
  },
1463
1482
  "Measures.Scale": {
1464
1483
  "Type": "Edm.Byte",
1465
1484
  "AppliesTo": [
1485
+ "Parameter",
1466
1486
  "Property"
1467
1487
  ]
1468
1488
  },
1469
1489
  "Measures.Unit": {
1470
1490
  "Type": "Edm.String",
1471
1491
  "AppliesTo": [
1492
+ "Parameter",
1472
1493
  "Property"
1473
1494
  ]
1474
1495
  },
1475
1496
  "Measures.UNECEUnit": {
1476
1497
  "Type": "Edm.String",
1477
1498
  "AppliesTo": [
1499
+ "Parameter",
1478
1500
  "Property"
1479
1501
  ]
1480
1502
  },
1481
1503
  "Measures.DurationGranularity": {
1482
1504
  "Type": "Measures.DurationGranularityType",
1483
1505
  "AppliesTo": [
1506
+ "Parameter",
1484
1507
  "Property"
1485
1508
  ]
1486
1509
  },
@@ -3360,6 +3383,10 @@
3360
3383
  "$kind": "TypeDefinition",
3361
3384
  "UnderlyingType": "Edm.String"
3362
3385
  },
3386
+ "Core.QualifiedBoundOperationName": {
3387
+ "$kind": "TypeDefinition",
3388
+ "UnderlyingType": "Edm.String"
3389
+ },
3363
3390
  "Core.LocalDateTime": {
3364
3391
  "$kind": "TypeDefinition",
3365
3392
  "UnderlyingType": "Edm.String"
@@ -3455,6 +3482,7 @@
3455
3482
  "Border": "Edm.Boolean",
3456
3483
  "FitToPage": "Edm.Boolean",
3457
3484
  "Padding": "Edm.Boolean",
3485
+ "HeaderFooter": "Edm.Boolean",
3458
3486
  "ResultSizeDefault": "Edm.Int32",
3459
3487
  "ResultSizeMaximum": "Edm.Int32"
3460
3488
  }
@@ -3939,7 +3967,7 @@
3939
3967
  "$kind": "ComplexType",
3940
3968
  "BaseType": "UI.DataFieldForActionAbstract",
3941
3969
  "Properties": {
3942
- "Action": "Common.ActionOverload",
3970
+ "Action": "UI.ActionName",
3943
3971
  "InvocationGrouping": "UI.OperationGroupingType",
3944
3972
  "Inline": "Edm.Boolean",
3945
3973
  "Determining": "Edm.Boolean",
@@ -3994,7 +4022,7 @@
3994
4022
  "BaseType": "UI.DataField",
3995
4023
  "Properties": {
3996
4024
  "Value": "Edm.PrimitiveType",
3997
- "Action": "Common.QualifiedName",
4025
+ "Action": "UI.ActionName",
3998
4026
  "Label": "Edm.String",
3999
4027
  "Criticality": "UI.CriticalityType",
4000
4028
  "CriticalityRepresentation": "UI.CriticalityRepresentationType",
@@ -4258,6 +4286,10 @@
4258
4286
  "Symbols": {}
4259
4287
  }
4260
4288
  },
4289
+ "UI.ActionName": {
4290
+ "$kind": "TypeDefinition",
4291
+ "UnderlyingType": "Edm.String"
4292
+ },
4261
4293
  "Validation.AllowedValue": {
4262
4294
  "$kind": "ComplexType",
4263
4295
  "Properties": {
@@ -1 +1 @@
1
- 7c3a66be27eceea68bf48c9f4eba8164
1
+ 6c1cc14fd8155dcb48908afb59568b42