@sap/cds-compiler 3.0.0 → 3.1.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 (79) hide show
  1. package/CHANGELOG.md +104 -9
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +28 -16
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +24 -2
  7. package/doc/CHANGELOG_DEPRECATED.md +21 -1
  8. package/lib/api/main.js +92 -40
  9. package/lib/api/options.js +2 -3
  10. package/lib/base/keywords.js +64 -1
  11. package/lib/base/message-registry.js +33 -5
  12. package/lib/base/messages.js +54 -65
  13. package/lib/base/model.js +2 -0
  14. package/lib/base/optionProcessorHelper.js +53 -21
  15. package/lib/checks/actionsFunctions.js +8 -7
  16. package/lib/checks/selectItems.js +96 -14
  17. package/lib/checks/types.js +5 -8
  18. package/lib/checks/validator.js +1 -2
  19. package/lib/compiler/assert-consistency.js +65 -13
  20. package/lib/compiler/base.js +6 -4
  21. package/lib/compiler/builtins.js +93 -4
  22. package/lib/compiler/checks.js +1 -1
  23. package/lib/compiler/define.js +28 -23
  24. package/lib/compiler/extend.js +20 -11
  25. package/lib/compiler/finalize-parse-cdl.js +5 -9
  26. package/lib/compiler/index.js +2 -0
  27. package/lib/compiler/populate.js +37 -32
  28. package/lib/compiler/propagator.js +11 -6
  29. package/lib/compiler/resolve.js +15 -19
  30. package/lib/compiler/shared.js +54 -18
  31. package/lib/compiler/tweak-assocs.js +5 -11
  32. package/lib/compiler/utils.js +15 -6
  33. package/lib/edm/annotations/genericTranslation.js +12 -2
  34. package/lib/edm/annotations/preprocessAnnotations.js +18 -15
  35. package/lib/edm/csn2edm.js +18 -17
  36. package/lib/edm/edm.js +22 -13
  37. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  38. package/lib/edm/edmInboundChecks.js +85 -0
  39. package/lib/edm/edmPreprocessor.js +336 -665
  40. package/lib/edm/edmUtils.js +86 -45
  41. package/lib/gen/Dictionary.json +29 -9
  42. package/lib/gen/language.checksum +1 -1
  43. package/lib/gen/language.interp +1 -2
  44. package/lib/gen/languageLexer.js +3 -0
  45. package/lib/gen/languageParser.js +4332 -4496
  46. package/lib/inspect/.eslintrc.json +4 -0
  47. package/lib/inspect/index.js +14 -0
  48. package/lib/inspect/inspectModelStatistics.js +81 -0
  49. package/lib/inspect/inspectPropagation.js +189 -0
  50. package/lib/inspect/inspectUtils.js +44 -0
  51. package/lib/json/from-csn.js +19 -20
  52. package/lib/json/to-csn.js +11 -8
  53. package/lib/language/genericAntlrParser.js +150 -92
  54. package/lib/language/language.g4 +47 -74
  55. package/lib/main.d.ts +1 -0
  56. package/lib/model/api.js +1 -1
  57. package/lib/model/csnRefs.js +56 -29
  58. package/lib/model/csnUtils.js +29 -14
  59. package/lib/model/revealInternalProperties.js +6 -4
  60. package/lib/modelCompare/compare.js +3 -0
  61. package/lib/optionProcessor.js +81 -38
  62. package/lib/render/toCdl.js +57 -32
  63. package/lib/render/toHdbcds.js +1 -1
  64. package/lib/render/toSql.js +31 -11
  65. package/lib/render/utils/common.js +3 -4
  66. package/lib/transform/db/associations.js +43 -35
  67. package/lib/transform/db/cdsPersistence.js +0 -1
  68. package/lib/transform/db/flattening.js +3 -4
  69. package/lib/transform/db/transformExists.js +7 -5
  70. package/lib/transform/draft/db.js +1 -1
  71. package/lib/transform/forHanaNew.js +11 -2
  72. package/lib/transform/forOdataNew.js +4 -4
  73. package/lib/transform/localized.js +15 -11
  74. package/lib/transform/odata/typesExposure.js +14 -5
  75. package/lib/utils/file.js +28 -18
  76. package/lib/utils/moduleResolve.js +0 -1
  77. package/package.json +3 -4
  78. package/share/messages/syntax-expected-integer.md +9 -8
  79. package/lib/checks/unknownMagic.js +0 -41
@@ -5,6 +5,7 @@
5
5
 
6
6
  const { searchName } = require('../base/messages');
7
7
  const { dictAddArray } = require('../base/dictionaries');
8
+ const { isDeprecatedEnabled } = require('../base/model');
8
9
 
9
10
  const {
10
11
  setLink,
@@ -144,6 +145,13 @@ function fns( model ) {
144
145
  rewrite: {
145
146
  next: '_$next', dollar: true, escape: 'param', noDep: true, rewrite: true,
146
147
  }, // TODO: assertion that there is no next/escape used
148
+ 'order-by': {
149
+ next: '_$next',
150
+ dollar: true,
151
+ escape: 'param',
152
+ assoc: 'nav',
153
+ deprecatedSourceRefs: true,
154
+ },
147
155
  'order-by-union': {
148
156
  next: '_$next', dollar: true, escape: 'param', noDep: true, noExt: true,
149
157
  },
@@ -160,6 +168,7 @@ function fns( model ) {
160
168
  resolveUncheckedPath,
161
169
  resolveTypeArgumentsUnchecked,
162
170
  resolvePath,
171
+ checkAnnotate,
163
172
  defineAnnotations,
164
173
  attachAndEmitValidNames,
165
174
  } );
@@ -602,6 +611,21 @@ function fns( model ) {
602
611
  else if (r) {
603
612
  return setArtifactLink( head, r );
604
613
  }
614
+ else if (spec.deprecatedSourceRefs && env._combined &&
615
+ isDeprecatedEnabled( options, 'autoCorrectOrderBySourceRefs' )) {
616
+ // User has provided a source element without table alias where a query
617
+ // element is expected. Possible on many DBs (and compiler v1), in CAP
618
+ // only with table alias. Auto-correct it if no duplicate.
619
+ // TODO: we could use that info also in messages when the deprecated flag is not set
620
+ const s = env._combined[head.id];
621
+ if (s && !Array.isArray(s)) {
622
+ path.$prefix = s.name.alias; // pushing it to path directly could be problematic
623
+ warning( null, [ head.location, user ],
624
+ { id: head.id, newcode: `${ s.name.alias }.${ head.id }` },
625
+ 'Replace source element reference $(ID) by $(NEWCODE); auto-corrected' );
626
+ return setArtifactLink( head, s );
627
+ }
628
+ }
605
629
  }
606
630
  if (spec.noMessage || msgArt === true && extDict === model.definitions)
607
631
  return null;
@@ -861,29 +885,41 @@ function fns( model ) {
861
885
  }
862
886
  }
863
887
 
888
+ // Issue messages for annotations on namespaces and builtins
889
+ // (TODO: really here?, probably split main artifacts vs returns)
890
+ // see also lateExtensions() where similar messages are reported
891
+ function checkAnnotate( construct, art ) {
892
+ // Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
893
+ // they can still be applied. Namespace annotations are extracted in to-csn.js
894
+ // In parseCdl mode USINGs and other unknown references are generated as
895
+ // namespaces which would lead to false positives.
896
+ // TODO: should this really be different to annotate-unknown?
897
+ if (art.kind === 'namespace') {
898
+ info( 'anno-namespace', [ construct.name.location, construct ], {},
899
+ 'Namespaces can\'t be annotated' );
900
+ }
901
+ // Builtin annotations would also get lost. Same as for namespaces:
902
+ // extracted in to-csn.js
903
+ else if (art.builtin === true) {
904
+ info( 'anno-builtin', [ construct.name.location, construct ], {},
905
+ 'Builtin types should not be annotated. Use custom type instead' );
906
+ }
907
+ else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
908
+ art.kind !== 'function' ) {
909
+ // `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
910
+ // for non-actions. We can't only check for !art.returns, because `action A();` is valid.
911
+ // `art._block` ensures that `art` is a defined def.
912
+ warning('anno-unexpected-returns', [ construct.name.location, construct ],
913
+ { keyword: 'returns', kind: art.kind }, 'Unexpected $(KEYWORD) for $(KIND)');
914
+ }
915
+ }
916
+
864
917
  // Set _block links for annotations (necessary for layering).
865
- // Issue messages for annotations on namespaces and builtins (TODO: really here?)
866
918
  // Also copy annotations from `construct` to `art` (TODO: separate that functionality).
867
919
  function defineAnnotations( construct, art, block, priority = false ) {
868
- if (!options.parseCdl && construct.kind === 'annotate') {
869
- // Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
870
- // they can still be applied. Namespace annotations are extracted in to-csn.js
871
- // In parseCdl mode USINGs and other unknown references are generated as
872
- // namespaces which would lead to false positives.
873
- // TODO: should this really be different to annotate-unknown?
874
- if (art.kind === 'namespace') {
875
- info( 'anno-namespace', [ construct.name.location, construct ], {},
876
- 'Namespaces can\'t be annotated' );
877
- }
878
- // Builtin annotations would also get lost. Same as for namespaces:
879
- // extracted in to-csn.js
880
- else if (art.builtin === true) {
881
- info( 'anno-builtin', [ construct.name.location, construct ], {},
882
- 'Builtin types should not be annotated. Use custom type instead' );
883
- }
884
- }
885
920
  if (construct.doc)
886
921
  art.doc = construct.doc; // e.g. through `extensions` array in CSN
922
+
887
923
  // set _block (for layering) and $priority, shallow-copy from extension
888
924
  // TODO: think of removing $priority, then
889
925
  // no _block: define, _block: annotate/extend/edmx
@@ -3,7 +3,6 @@
3
3
  'use strict';
4
4
 
5
5
  const {
6
- isDeprecatedEnabled,
7
6
  forEachDefinition,
8
7
  forEachGeneric,
9
8
  forEachInOrder,
@@ -26,7 +25,6 @@ const $location = Symbol.for('cds.$location');
26
25
 
27
26
  // Export function of this file.
28
27
  function tweakAssocs( model ) {
29
- const { options } = model;
30
28
  // Get shared functionality and the message function:
31
29
  const {
32
30
  info, warning, error,
@@ -38,11 +36,6 @@ function tweakAssocs( model ) {
38
36
  } = model.$functions;
39
37
  const { environment } = model.$volatileFunctions;
40
38
 
41
- // behavior depending on option `deprecated`:
42
- const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
43
- // TODO: we should get rid of noElementsExpansion soon; both
44
- // beta.nestedProjections and beta.universalCsn do not work with it.
45
-
46
39
  // Phase 5: rewrite associations
47
40
  forEachDefinition( model, rewriteSimple );
48
41
  // TODO: sequence not good enough with derived type of structure with
@@ -113,7 +106,7 @@ function tweakAssocs( model ) {
113
106
 
114
107
  function rewriteAssociationCheck( element ) {
115
108
  const elem = element.items || element; // TODO v2: nested items
116
- if (elem.elements && enableExpandElements)
109
+ if (elem.elements)
117
110
  forEachGeneric( elem, 'elements', rewriteAssociationCheck );
118
111
  if (!elem.target)
119
112
  return;
@@ -207,7 +200,7 @@ function tweakAssocs( model ) {
207
200
 
208
201
  function rewriteAssociation( element ) {
209
202
  let elem = element.items || element; // TODO v2: nested items
210
- if (elem.elements && enableExpandElements)
203
+ if (elem.elements)
211
204
  forEachGeneric( elem, 'elements', rewriteAssociation );
212
205
  if (!originTarget( elem ))
213
206
  return;
@@ -286,7 +279,7 @@ function tweakAssocs( model ) {
286
279
  // same (TODO later: set status whether rewrite changes anything),
287
280
  // especially problematic are refs starting with $self:
288
281
  setExpandStatus( elem, 'target' );
289
- if (enableExpandElements && elem._parent && elem._parent.kind === 'element') {
282
+ if (elem._parent && elem._parent.kind === 'element') {
290
283
  // managed association as sub element not supported yet
291
284
  error( null, [ elem.location, elem ], {},
292
285
  // eslint-disable-next-line max-len
@@ -369,7 +362,8 @@ function tweakAssocs( model ) {
369
362
  const item = expr.path[root.kind === '$self' ? 1 : 0];
370
363
  if (!item)
371
364
  return; // just $self
372
- const elem = assoc._main.elements[item.id]; // corresponding elem in including structure
365
+ // corresponding elem in including structure
366
+ const elem = (assoc._main.items || assoc._main).elements[item.id];
373
367
  if (!(Array.isArray(elem) || // no msg for redefs
374
368
  elem === item._artifact || // redirection for explicit def
375
369
  elem._origin === item._artifact)) {
@@ -95,6 +95,14 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
95
95
  return elem;
96
96
  }
97
97
 
98
+ /**
99
+ * Set the member `elem` to have a _parent link to `parent` and a corresponding
100
+ * _main link. Also set the member's name accordingly, where argument `name`
101
+ * is most often the property `elem.name.id`.
102
+ *
103
+ * If argument `prop` is provided, add `elem` to the dictionary of that name,
104
+ * e.g. `elements`.
105
+ */
98
106
  function setMemberParent( elem, name, parent, prop ) {
99
107
  if (prop) { // extension or structure include
100
108
  // TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
@@ -107,9 +115,10 @@ function setMemberParent( elem, name, parent, prop ) {
107
115
  parent = parent._outer;
108
116
  setLink( elem, '_parent', parent );
109
117
  setLink( elem, '_main', parent._main || parent );
110
- const parentName = parent.name || parent._outer.name;
111
- elem.name.absolute = parentName.absolute;
112
- if (name == null)
118
+ const parentName = parent.name || parent._outer?.name;
119
+ if (parentName) // may not be available in e.g. cast() - TODO recheck (#9503)
120
+ elem.name.absolute = parentName.absolute;
121
+ if (!parentName || name == null)
113
122
  return;
114
123
  const normalized = kindProperties[elem.kind].normalized || elem.kind;
115
124
  [ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
@@ -188,8 +197,8 @@ function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = fa
188
197
  *
189
198
  * @param {XSN.Path} path
190
199
  */
191
- function pathName(path) {
192
- return (path.broken) ? '' : path.map( id => id.id ).join('.');
200
+ function pathName( path ) {
201
+ return (path && !path.broken) ? path.map( id => id.id ).join('.') : '';
193
202
  }
194
203
 
195
204
  /**
@@ -357,7 +366,7 @@ function traverseQueryExtra( main, callback ) {
357
366
  // that value is only on elements, types, and params -> no other members
358
367
  // when set, only on elem/art with expanded elements
359
368
  // - 'target': all expanded (sub) elements might only have new target/on, but
360
- // no indivual annotations on any (sub) member
369
+ // no individual annotations on any (sub) member
361
370
  // when set, traverse all parents where the value has been 'origin' before
362
371
  // - 'annotate': at least one inferred (sub) member has an individual annotation,
363
372
  // not counting propagated ones; set up to the definition (main artifact)
@@ -401,8 +401,6 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
401
401
  params.push(action['@cds.odata.bindingparameter.collection'] ? 'Collection(' + bindingParam + ')' : bindingParam);
402
402
  }
403
403
  if (action.kind === 'function') {
404
- let mapType = (p) => (isBuiltinType(p.type)) ?
405
- edmUtils.mapCdsToEdmType(p, messageFunctions, false /*is only called for v4*/) : p.type;
406
404
  if(action.params) {
407
405
  action.params && Object.values(action.params).forEach(p => {
408
406
  let isArrayType = !p.type && p.items && p.items.type;
@@ -411,6 +409,18 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
411
409
  }
412
410
  }
413
411
  return '(' + params.join(',') + ')';
412
+
413
+ function mapType(p) {
414
+ if(isBuiltinType(p.type))
415
+ return edmUtils.mapCdsToEdmType(p, messageFunctions, false /*is only called for v4*/)
416
+ else if(options.whatsMySchemaName) {
417
+ const schemaName = options.whatsMySchemaName(p.type);
418
+ // strip the service namespace of from a parameter type
419
+ if(schemaName && schemaName !== options.serviceName)
420
+ return p.type.replace(options.serviceName + '.', '');
421
+ }
422
+ return p.type;
423
+ }
414
424
  }
415
425
 
416
426
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const edmUtils = require('../edmUtils.js');
4
3
  const { makeMessageFunction } = require('../../base/messages.js');
4
+ const { forEachDefinition } = require('../../model/csnUtils.js');
5
5
 
6
6
 
7
7
  /**************************************************************************************************
@@ -37,15 +37,14 @@ function preprocessAnnotations(csn, serviceName, options) {
37
37
  let targetName = (typeof assoc.target === 'object') ? assoc.target.name : assoc.target;
38
38
  let target = (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
39
39
 
40
- let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key);
40
+ let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
41
41
  if (keyNames.length === 0) {
42
42
  keyNames.push('MISSING');
43
43
  warning(null, null, `in annotation preprocessing: target ${targetName} has no key`);
44
44
  }
45
45
  else if (keyNames.length > 1)
46
46
  warning(null, null, `in annotation preprocessing: target ${targetName} has multiple key elements`);
47
-
48
- // TODO: what happens if key of target is itself a managed association?
47
+
49
48
  return keyNames[0];
50
49
  }
51
50
 
@@ -58,15 +57,15 @@ function preprocessAnnotations(csn, serviceName, options) {
58
57
  function resolveShortcuts() {
59
58
  let art = null;
60
59
 
61
- edmUtils.forAll(csn.definitions, (artifact, artifactName) => {
62
- if(artifactName == serviceName || artifactName.startsWith(serviceName + '.')) {
60
+ forEachDefinition(csn, (artifact, artifactName) => {
61
+ if(artifactName === serviceName || artifactName.startsWith(serviceName + '.')) {
63
62
  art = artifactName;
64
63
  handleAnnotations(artifactName, artifact);
65
- edmUtils.forAll(artifact.elements, (element, elementName) => {
64
+ artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
66
65
  handleAnnotations(elementName, element);
67
66
  });
68
- edmUtils.forAll(artifact.actions, (action) => {
69
- edmUtils.forAll(action.params, (param, paramName) => {
67
+ artifact.actions && Object.values(artifact.actions).forEach(action => {
68
+ action.params && Object.entries(action.params).forEach(([paramName, param]) => {
70
69
  handleAnnotations(paramName, param);
71
70
  });
72
71
  });
@@ -155,7 +154,7 @@ function preprocessAnnotations(csn, serviceName, options) {
155
154
  return false;
156
155
  }
157
156
  let assoc = csn.definitions[art].elements[assocName];
158
- if (!assoc || !(assoc.type === 'cds.Association' || assoc.type === 'cds.Composition')) {
157
+ if (!assoc || !assoc.target) {
159
158
  warning(null, null, `in annotation preprocessing/${aNameWithoutQualifier}: there is no association "${assocName}", ${ctx}`);
160
159
  return false;
161
160
  }
@@ -165,7 +164,7 @@ function preprocessAnnotations(csn, serviceName, options) {
165
164
  }
166
165
  else if (aNameWithoutQualifier === '@Common.ValueList.entity') {
167
166
  // if both annotations are present, ignore 'entity' and raise a message
168
- if (annoNames.map(x=>x.split('#')[0]).find(x=>(x=='@Common.ValueList.viaAssociation'))) {
167
+ if (annoNames.map(x=>x.split('#')[0]).find(x=>(x==='@Common.ValueList.viaAssociation'))) {
169
168
  warning(null, null, `in annotation preprocessing/@Common.ValueList: 'entity' is ignored, as 'viaAssociation' is present, ${ctx}`);
170
169
  return false;
171
170
  }
@@ -196,7 +195,7 @@ function preprocessAnnotations(csn, serviceName, options) {
196
195
  // name of the element carrying the value help annotation
197
196
  // if this is a managed assoc, use fk field instead (if there is a single one)
198
197
  let localDataProp = carrierName.split('/').pop();
199
- if (edmUtils.isManagedAssociation(carrier)) {
198
+ if (carrier.target && carrier.on === undefined) {
200
199
  localDataProp = localDataProp + fkSeparator + getKeyOfTargetOfManagedAssoc(carrier);
201
200
  }
202
201
 
@@ -212,7 +211,7 @@ function preprocessAnnotations(csn, serviceName, options) {
212
211
  // valueListProp: the (single) key field of the value list entity
213
212
  // if no key or multiple keys -> warning
214
213
  let valueListProp = null;
215
- let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key );
214
+ let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key && !vlEntity.elements[x].target );
216
215
  if (keys.length === 0) {
217
216
  warning(null, null, `in annotation preprocessing/value help shortcut: entity "${enameFull}" has no key, ${ctx}`);
218
217
  return false;
@@ -297,8 +296,12 @@ function preprocessAnnotations(csn, serviceName, options) {
297
296
 
298
297
  //change the scalar anno into a "pseudo-structured" one
299
298
  // TODO should be flattened, but then alphabetical order is destroyed
300
- let newTextAnno = { '$value': textAnno, '@UI.TextArrangement': value };
301
- carrier['@Common.Text'] = newTextAnno;
299
+
300
+ // Do not overwrite existing nested annotation values, instead give existing
301
+ // nested annotation precedence and remove outer annotation (always)
302
+ if(!carrier['@Common.Text.@UI.TextArrangement'] && textAnno) {
303
+ carrier['@Common.Text'] = { '$value': textAnno, '@UI.TextArrangement': value };
304
+ }
302
305
  delete carrier[aName];
303
306
  }
304
307
  }
@@ -7,7 +7,7 @@ const NAVPROP_TRENNER = '_';
7
7
  const VALUELIST_NAVPROP_PREFIX = '';
8
8
 
9
9
  const edmUtils = require('./edmUtils.js')
10
- const { initializeModel, assignAnnotation } = require('./edmPreprocessor.js');
10
+ const { initializeModel } = require('./edmPreprocessor.js');
11
11
  const translate = require('./annotations/genericTranslation.js');
12
12
  const { setProp } = require('../base/model');
13
13
  const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
@@ -326,15 +326,16 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
326
326
 
327
327
  /* create the entitytypes and sets
328
328
  Do not create an entity set if:
329
- V4 containment: _containerEntity is set and not equal with the artifact name
329
+ V4 containment: $containerNames is set and not equal with the artifact name
330
330
  Entity starts with 'localserviceNameized.' or ends with '_localized'
331
331
  */
332
332
  edmUtils.foreach(schemaCsn.definitions,
333
- a => edmUtils.isEntity(a) && !a.abstract && a.name.startsWith(schemaNamePrefix),
333
+ a => a.kind === 'entity' && !a.abstract && a.name.startsWith(schemaNamePrefix),
334
334
  createEntityTypeAndSet
335
335
  );
336
336
  // create unbound actions/functions
337
- edmUtils.foreach(schemaCsn.definitions, a => edmUtils.isActionOrFunction(a) && a.name.startsWith(schemaNamePrefix),
337
+ edmUtils.foreach(schemaCsn.definitions,
338
+ a => (a.kind === 'action' || a.kind === 'function') && a.name.startsWith(schemaNamePrefix),
338
339
  (options.isV4()) ? createActionV4 : createActionV2);
339
340
 
340
341
  // create the complex types
@@ -346,7 +347,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
346
347
  {
347
348
  edmUtils.foreach(schemaCsn.definitions,
348
349
  artifact => edmUtils.isDerivedType(artifact) &&
349
- !edmUtils.isAssociationOrComposition(artifact) &&
350
+ !artifact.target &&
350
351
  artifact.name.startsWith(schemaNamePrefix),
351
352
  createTypeDefinition);
352
353
  }
@@ -414,7 +415,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
414
415
  if(p._edmAttributes.Name === EntityTypeName)
415
416
  warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
416
417
 
417
- if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn))
418
+ if(options.isV2() && p._isCollection && !p._csn.target)
418
419
  warning('odata-spec-violation-array', pLoc, { version: '2.0' });
419
420
 
420
421
  if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
@@ -432,7 +433,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
432
433
  // CDXCORE-CDXCORE-173
433
434
  if(options.isV2() && hasStream) {
434
435
  attributes['m:HasStream'] = true;
435
- assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
436
+ edmUtils.assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
436
437
  }
437
438
 
438
439
  Schema.append(new Edm.EntityType(v, attributes, properties, entityCsn));
@@ -461,7 +462,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
461
462
  }
462
463
 
463
464
  // put actions behind entity types in Schema/EntityContainer
464
- edmUtils.forAll(entityCsn.actions, (a, n) => {
465
+ entityCsn.actions && Object.entries(entityCsn.actions).forEach(([ n, a ]) => {
465
466
  (options.isV4()) ? createActionV4(a, n, entityCsn)
466
467
  : createActionV2(a, n, entityCsn)
467
468
  });
@@ -522,7 +523,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
522
523
  }
523
524
 
524
525
  // Parameter Nodes
525
- edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
526
+ actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
526
527
  const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
527
528
  const pLoc = [ ...loc, 'params', p._edmAttributes.Name ];
528
529
  if(!edmUtils.isODataSimpleIdentifier(parameterName))
@@ -573,7 +574,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
573
574
  if(rt) // add EntitySet attribute only if return type is an entity
574
575
  {
575
576
  const defintion = schemaCsn.definitions[rt];
576
- if(defintion && edmUtils.isEntity(defintion))
577
+ if(defintion && defintion.kind === 'entity')
577
578
  {
578
579
  functionImport.setEdmAttribute('EntitySet', rt.replace(schemaNamePrefix, ''));
579
580
  }
@@ -597,7 +598,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
597
598
  // Binding Parameter: Primary Keys at first position in sequence, this is decisive!
598
599
  // V2 XML: Nullable=false is set because we reuse the primary key property for the parameter
599
600
  edmUtils.foreach(entityCsn.elements,
600
- elementCsn => elementCsn.key && !edmUtils.isAssociationOrComposition(elementCsn),
601
+ elementCsn => elementCsn.key && !elementCsn.target,
601
602
  (elementCsn, elementName) => {
602
603
  functionImport.append(new Edm.Parameter(v, { Name: elementName }, elementCsn, 'In' ));
603
604
  }
@@ -605,7 +606,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
605
606
  }
606
607
 
607
608
  // is this still required?
608
- edmUtils.forAll(actionCsn, (v, p) => {
609
+ Object.entries(actionCsn).forEach(([p, v]) => {
609
610
  if (p.match(/^@sap\./))
610
611
  functionImport.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v });
611
612
  });
@@ -613,7 +614,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
613
614
  // V2 XML: Parameters that are not explicitly marked as Nullable or NotNullable in the CSN must become Nullable=true
614
615
  // V2 XML spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
615
616
  // the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
616
- edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
617
+ actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
617
618
  const pLoc = [...loc, 'params', parameterName];
618
619
  const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
619
620
  edmTypeCompatibilityCheck(param, pLoc);
@@ -683,12 +684,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
683
684
  let hasStream = false;
684
685
  const streamProps = [];
685
686
 
686
- edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
687
+ elementsCsn.elements && Object.entries(elementsCsn.elements).forEach(([elementName, elementCsn]) =>
687
688
  {
688
689
  if(elementCsn._edmParentCsn == undefined)
689
690
  setProp(elementCsn, '_edmParentCsn', edmParentCsn);
690
691
 
691
- if(edmUtils.isAssociationOrComposition(elementCsn)) {
692
+ if(elementCsn.target) {
692
693
  // Foreign keys are part of the generic elementCsn.elements property creation
693
694
 
694
695
  // This is the V4 edmx:NavigationProperty
@@ -769,10 +770,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
769
770
  message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
770
771
 
771
772
  if(options.isV2()) {
772
- if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn))
773
+ if(p._isCollection && !p._csn.target)
773
774
  warning('odata-spec-violation-array', pLoc, { version: '2.0' });
774
775
 
775
- if(edmUtils.isAssociationOrComposition(p._csn))
776
+ if(p._csn.target)
776
777
  warning('odata-spec-violation-assoc', pLoc, { version: '2.0' });
777
778
  }
778
779
  });
package/lib/edm/edm.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const edmUtils = require('./edmUtils.js');
4
4
  const { isBuiltinType } = require('../model/csnUtils.js');
5
5
  const { forEach } = require("../utils/objectUtils");
6
+ const { isBetaEnabled } = require('../base/model.js');
6
7
 
7
8
  // facet definitions, optional could either be true or array of edm types
8
9
  // remove indicates wether or not the canonic facet shall be removed when applying @odata.Type
@@ -239,7 +240,7 @@ function getEdm(options, messageFunctions) {
239
240
  if(csn)
240
241
  {
241
242
  const attr = (useSetAttributes ? csn._SetAttributes : csn);
242
- edmUtils.forAll(attr, (v, p) => {
243
+ attr && Object.entries(attr).forEach(([p, v]) => {
243
244
  if (p.match(/^@sap./))
244
245
  this.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v } );
245
246
  });
@@ -332,7 +333,7 @@ function getEdm(options, messageFunctions) {
332
333
  if(what==='metadata' || what==='all')
333
334
  {
334
335
  xml += super.innerXML(indent);
335
- edmUtils.forAll(this._actions, actionArray => {
336
+ this._actions && Object.values(this._actions).forEach(actionArray => {
336
337
  actionArray.forEach(action => {
337
338
  xml += action.toXML(indent, what) + '\n'; });
338
339
  });
@@ -350,7 +351,7 @@ function getEdm(options, messageFunctions) {
350
351
  // no $Namespace
351
352
  toJSONattributes(json)
352
353
  {
353
- edmUtils.forAll(this._edmAttributes, (v,p) => {
354
+ this._edmAttributes && Object.entries(this._edmAttributes).forEach(([p, v]) => {
354
355
  if (p !== 'Name' && p !== 'Namespace')
355
356
  json[p[0] === '@' ? p : '$' + p] = v;
356
357
  });
@@ -371,7 +372,7 @@ function getEdm(options, messageFunctions) {
371
372
  if(Object.keys(json_Annotations).length)
372
373
  json['$Annotations'] = json_Annotations;
373
374
  }
374
- edmUtils.forAll(this._actions, (actionArray, actionName) => {
375
+ this._actions && Object.entries(this._actions).forEach(([actionName, actionArray]) => {
375
376
  json[actionName] = [];
376
377
  actionArray.forEach(action => {
377
378
  json[actionName].push(action.toJSON());
@@ -737,7 +738,7 @@ function getEdm(options, messageFunctions) {
737
738
  if(this._type !== 'Edm.String' && this._type) // Edm.String is default)
738
739
  json['$'+this._typeName] = this._type;
739
740
 
740
- edmUtils.forAll(this._edmAttributes, (v,p) => {
741
+ this._edmAttributes && Object.entries(this._edmAttributes).forEach(([p, v]) => {
741
742
  if (p !== 'Name' && p !== this._typeName
742
743
  // remove this line if Nullable=true becomes default
743
744
  && !(p === 'Nullable' && v == false))
@@ -754,7 +755,14 @@ function getEdm(options, messageFunctions) {
754
755
  }
755
756
  }
756
757
 
757
- class ComplexType extends TypeBase { }
758
+ class ComplexType extends TypeBase {
759
+ constructor(v, details, csn) {
760
+ super(v, details, csn);
761
+ if(this.v4 && !!csn['@open'] && isBetaEnabled(options, 'odataOpenType')) {
762
+ this._edmAttributes['OpenType'] = true;
763
+ }
764
+ }
765
+ }
758
766
  class EntityType extends ComplexType
759
767
  {
760
768
  constructor(v, details, properties, csn)
@@ -840,8 +848,8 @@ function getEdm(options, messageFunctions) {
840
848
  super(v, attributes, csn);
841
849
 
842
850
  // array of enum not yet allowed
843
- let enumValues = /*(csn.items && csn.items.enum) ||*/ csn.enum;
844
- edmUtils.forAll(enumValues, (e, en) => {
851
+ const enumValues = /*(csn.items && csn.items.enum) ||*/ csn.enum;
852
+ enumValues && Object.entries(enumValues).forEach(([en, e]) => {
845
853
  this.append(new Member(v, { Name: en, Value: e.val } ));
846
854
  });
847
855
  }
@@ -1177,9 +1185,10 @@ function getEdm(options, messageFunctions) {
1177
1185
  _constraints = this._csn._constraints._partnerCsn._constraints;
1178
1186
  [i,j] = [1,0];
1179
1187
  }
1180
- edmUtils.forAll(_constraints.constraints,
1181
- c => this.append(new ReferentialConstraint(this._v,
1182
- { Property: c[i].join(options.pathDelimiter), ReferencedProperty: c[j].join(options.pathDelimiter) } ) ) );
1188
+ _constraints.constraints && Object.values(_constraints.constraints).forEach(c =>
1189
+ this.append(new ReferentialConstraint(this._v,
1190
+ { Property: c[i].join(options.pathDelimiter), ReferencedProperty: c[j].join(options.pathDelimiter) } ) )
1191
+ );
1183
1192
  }
1184
1193
  }
1185
1194
 
@@ -1502,7 +1511,7 @@ function getEdm(options, messageFunctions) {
1502
1511
  }
1503
1512
  toJSONattributes(json) {
1504
1513
  super.toJSONattributes(json);
1505
- edmUtils.forAll(this._jsonOnlyAttributes, (v,p) => {
1514
+ this._jsonOnlyAttributes && Object.entries(this._jsonOnlyAttributes).forEach(([p, v]) => {
1506
1515
  json[p[0] === '@' ? p : '$' + p] = v;
1507
1516
  });
1508
1517
  return json;
@@ -1606,7 +1615,7 @@ function getEdm(options, messageFunctions) {
1606
1615
  node._d = new Dependent(v, { Role: from } );
1607
1616
  node._p = new Principal(v, { Role: to } );
1608
1617
 
1609
- edmUtils.forAll(c, cv => {
1618
+ c && Object.values(c).forEach(cv => {
1610
1619
  node._d.append(new PropertyRef(v, cv[0].join(options.pathDelimiter)));
1611
1620
  node._p.append(new PropertyRef(v, cv[1].join(options.pathDelimiter)));
1612
1621
  });