@sap/cds-compiler 2.12.0 → 2.13.6

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 (118) hide show
  1. package/CHANGELOG.md +110 -15
  2. package/bin/cdsc.js +13 -13
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +28 -63
  8. package/lib/api/options.js +3 -3
  9. package/lib/api/validate.js +0 -5
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +25 -4
  16. package/lib/base/messages.js +16 -26
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +158 -123
  19. package/lib/checks/annotationsOData.js +1 -1
  20. package/lib/checks/cdsPersistence.js +2 -1
  21. package/lib/checks/enricher.js +17 -1
  22. package/lib/checks/invalidTarget.js +3 -1
  23. package/lib/checks/managedWithoutKeys.js +3 -1
  24. package/lib/checks/selectItems.js +4 -4
  25. package/lib/checks/sql-snippets.js +27 -26
  26. package/lib/checks/types.js +1 -1
  27. package/lib/checks/validator.js +4 -7
  28. package/lib/compiler/assert-consistency.js +5 -3
  29. package/lib/compiler/builtins.js +8 -6
  30. package/lib/compiler/checks.js +14 -3
  31. package/lib/compiler/cycle-detector.js +1 -1
  32. package/lib/compiler/define.js +1103 -0
  33. package/lib/compiler/extend.js +983 -0
  34. package/lib/compiler/finalize-parse-cdl.js +231 -0
  35. package/lib/compiler/index.js +32 -13
  36. package/lib/compiler/kick-start.js +190 -0
  37. package/lib/compiler/moduleLayers.js +4 -4
  38. package/lib/compiler/populate.js +1226 -0
  39. package/lib/compiler/propagator.js +111 -46
  40. package/lib/compiler/resolve.js +1433 -0
  41. package/lib/compiler/shared.js +64 -37
  42. package/lib/compiler/tweak-assocs.js +529 -0
  43. package/lib/compiler/utils.js +197 -33
  44. package/lib/edm/.eslintrc.json +5 -0
  45. package/lib/edm/annotations/genericTranslation.js +5 -9
  46. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  47. package/lib/edm/csn2edm.js +9 -8
  48. package/lib/edm/edm.js +11 -12
  49. package/lib/edm/edmPreprocessor.js +137 -73
  50. package/lib/edm/edmUtils.js +116 -22
  51. package/lib/gen/Dictionary.json +10 -3
  52. package/lib/gen/language.checksum +1 -1
  53. package/lib/gen/language.interp +9 -1
  54. package/lib/gen/language.tokens +86 -83
  55. package/lib/gen/languageLexer.interp +10 -1
  56. package/lib/gen/languageLexer.js +860 -833
  57. package/lib/gen/languageLexer.tokens +78 -75
  58. package/lib/gen/languageParser.js +5282 -4265
  59. package/lib/json/from-csn.js +12 -1
  60. package/lib/json/to-csn.js +126 -66
  61. package/lib/language/docCommentParser.js +2 -2
  62. package/lib/language/genericAntlrParser.js +76 -3
  63. package/lib/language/language.g4 +297 -130
  64. package/lib/language/multiLineStringParser.js +5 -5
  65. package/lib/main.d.ts +468 -59
  66. package/lib/main.js +35 -9
  67. package/lib/model/api.js +3 -1
  68. package/lib/model/csnRefs.js +225 -156
  69. package/lib/model/csnUtils.js +192 -223
  70. package/lib/model/enrichCsn.js +70 -29
  71. package/lib/model/revealInternalProperties.js +27 -6
  72. package/lib/model/sortViews.js +2 -1
  73. package/lib/modelCompare/compare.js +17 -12
  74. package/lib/optionProcessor.js +5 -4
  75. package/lib/render/manageConstraints.js +35 -32
  76. package/lib/render/toCdl.js +73 -288
  77. package/lib/render/toHdbcds.js +25 -23
  78. package/lib/render/toSql.js +98 -41
  79. package/lib/render/utils/common.js +5 -10
  80. package/lib/render/utils/sql.js +4 -3
  81. package/lib/render/utils/stringEscapes.js +111 -0
  82. package/lib/sql-identifier.js +1 -1
  83. package/lib/transform/.eslintrc.json +5 -0
  84. package/lib/transform/db/.eslintrc.json +2 -0
  85. package/lib/transform/db/applyTransformations.js +35 -12
  86. package/lib/transform/db/assertUnique.js +1 -1
  87. package/lib/transform/db/associations.js +103 -305
  88. package/lib/transform/db/cdsPersistence.js +2 -2
  89. package/lib/transform/db/constraints.js +55 -52
  90. package/lib/transform/db/expansion.js +46 -24
  91. package/lib/transform/db/flattening.js +553 -102
  92. package/lib/transform/db/groupByOrderBy.js +3 -1
  93. package/lib/transform/db/transformExists.js +59 -6
  94. package/lib/transform/db/views.js +5 -4
  95. package/lib/transform/draft/.eslintrc.json +38 -0
  96. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  97. package/lib/transform/draft/odata.js +227 -0
  98. package/lib/transform/forHanaNew.js +67 -183
  99. package/lib/transform/forOdataNew.js +17 -171
  100. package/lib/transform/localized.js +34 -19
  101. package/lib/transform/odata/generateForeignKeyElements.js +1 -1
  102. package/lib/transform/odata/referenceFlattener.js +95 -89
  103. package/lib/transform/odata/structureFlattener.js +1 -1
  104. package/lib/transform/odata/toFinalBaseType.js +86 -12
  105. package/lib/transform/odata/typesExposure.js +5 -5
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +36 -22
  108. package/lib/transform/translateAssocsToJoins.js +2 -19
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +170 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/objectUtils.js +30 -0
  114. package/package.json +1 -1
  115. package/share/messages/README.md +26 -0
  116. package/lib/compiler/definer.js +0 -2361
  117. package/lib/compiler/resolver.js +0 -3079
  118. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -6,6 +6,8 @@
6
6
  // Please do not add functions “for completeness”, this is not an API file for
7
7
  // others but only by the core compiler.
8
8
 
9
+ // TODO: probably split this file into utils/….js
10
+
9
11
  'use strict';
10
12
 
11
13
  const { dictAdd, pushToDict } = require('../base/dictionaries');
@@ -31,6 +33,8 @@ function annotationIsFalse( anno ) { // falsy, but not null (u
31
33
  }
32
34
 
33
35
  /**
36
+ * Set compiler-calculated annotation value.
37
+ *
34
38
  * @param {XSN.Artifact} art
35
39
  * @param {string} anno
36
40
  * @param {XSN.Location} [location]
@@ -44,11 +48,11 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
44
48
  name: { path: [ { id: anno.slice(1), location } ], location },
45
49
  val,
46
50
  literal,
51
+ $inferred: '$generated',
47
52
  location,
48
53
  };
49
54
  }
50
55
 
51
- // TODO: define setLink() like the current setProp(), we might have setArtifactLink()
52
56
  // Do not share this function with CSN processors!
53
57
 
54
58
  // The link (_artifact,_effectiveType,...) usually has the artifact as value.
@@ -57,28 +61,13 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
57
61
  // - null: no valid reference, param:true if that is not allowed
58
62
  // - false (only complete ref): multiple definitions, rejected
59
63
  // - 0 (for _effectiveType only): circular reference
60
- function setLink( obj, value = null, prop = '_artifact' ) {
64
+ function setLink( obj, prop, value ) {
61
65
  Object.defineProperty( obj, prop, { value, configurable: true, writable: true } );
62
66
  return value;
63
67
  }
64
-
65
- /**
66
- * Like `obj.prop = value`, but not contained in JSON / CSN
67
- * It's important to set enumerable explicitly to false (although 'false' is the default),
68
- * as else, if the property already exists, it keeps the old setting for enumerable.
69
- *
70
- * @param {object} obj
71
- * @param {string} prop
72
- * @param {any} value
73
- */
74
- function setProp(obj, prop, value) {
75
- const descriptor = {
76
- value,
77
- configurable: true,
78
- writable: true,
79
- enumerable: false,
80
- };
81
- Object.defineProperty( obj, prop, descriptor );
68
+ // And a variant with the most common `prop`:
69
+ function setArtifactLink( obj, value ) {
70
+ Object.defineProperty( obj, '_artifact', { value, configurable: true, writable: true } );
82
71
  return value;
83
72
  }
84
73
 
@@ -92,7 +81,7 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
92
81
  elem.name.$inferred = origin.name.$inferred;
93
82
  if (parent)
94
83
  setMemberParent( elem, name, parent, prop ); // TODO: redef in template
95
- setProp( elem, '_origin', origin );
84
+ setLink( elem, '_origin', origin );
96
85
  // TODO: should we use silent dependencies also for other things, like
97
86
  // included elements? (Currently for $inferred: 'expand-element' only)
98
87
  if (silentDep)
@@ -110,20 +99,21 @@ function setMemberParent( elem, name, parent, prop ) {
110
99
  p[prop] = Object.create(null);
111
100
  dictAdd( p[prop], name, elem );
112
101
  }
113
- if (parent._outer)
102
+ if (parent._outer && parent._outer.items) // TODO: remove for items, too
114
103
  parent = parent._outer;
115
- setProp( elem, '_parent', parent );
116
- setProp( elem, '_main', parent._main || parent );
117
- elem.name.absolute = elem._main.name.absolute;
104
+ setLink( elem, '_parent', parent );
105
+ setLink( elem, '_main', parent._main || parent );
106
+ const parentName = parent.name || parent._outer.name;
107
+ elem.name.absolute = parentName.absolute;
118
108
  if (name == null)
119
109
  return;
120
110
  const normalized = kindProperties[elem.kind].normalized || elem.kind;
121
111
  [ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
122
112
  if (normalized === kind)
123
- elem.name[kind] = (parent.name[kind] != null && kind !== 'select' && kind !== 'alias') ? `${ parent.name[kind] }.${ name }` : name;
113
+ elem.name[kind] = (parentName[kind] != null && kind !== 'select' && kind !== 'alias') ? `${ parentName[kind] }.${ name }` : name;
124
114
 
125
- else if (parent.name[kind] != null)
126
- elem.name[kind] = parent.name[kind];
115
+ else if (parentName[kind] != null)
116
+ elem.name[kind] = parentName[kind];
127
117
 
128
118
  else
129
119
  delete elem.name[kind];
@@ -140,7 +130,7 @@ function setMemberParent( elem, name, parent, prop ) {
140
130
  */
141
131
  function dependsOn( user, art, location ) {
142
132
  if (!user._deps)
143
- setProp( user, '_deps', [] );
133
+ setLink( user, '_deps', [] );
144
134
  user._deps.push( { art, location } );
145
135
  }
146
136
 
@@ -153,17 +143,17 @@ function dependsOn( user, art, location ) {
153
143
  */
154
144
  function dependsOnSilent( user, art ) {
155
145
  if (!user._deps)
156
- setProp( user, '_deps', [] );
146
+ setLink( user, '_deps', [] );
157
147
  user._deps.push( { art } );
158
148
  }
159
149
 
160
150
  function storeExtension( elem, name, prop, parent, block ) {
161
151
  if (prop === 'enum')
162
152
  prop = 'elements';
163
- setProp( elem, '_block', block );
153
+ setLink( elem, '_block', block );
164
154
  const kind = `_${ elem.kind }`; // _extend or _annotate
165
155
  if (!parent[kind])
166
- setProp( parent, kind, {} );
156
+ setLink( parent, kind, {} );
167
157
  // if (name === '' && prop === 'params') {
168
158
  // pushToDict( parent[kind], 'returns', elem ); // not really a dict
169
159
  // return;
@@ -218,6 +208,174 @@ function augmentPath( location, ...args ) {
218
208
  return { path: args.map( id => ({ id, location }) ), location };
219
209
  }
220
210
 
211
+ function copyExpr( expr, location, skipUnderscored, rewritePath ) {
212
+ if (!expr || typeof expr !== 'object')
213
+ return expr;
214
+ else if (Array.isArray(expr))
215
+ return expr.map( e => copyExpr( e, location, skipUnderscored, rewritePath ) );
216
+
217
+ const proto = Object.getPrototypeOf( expr );
218
+ if (proto && proto !== Object.prototype) // do not copy object from special classes
219
+ return expr;
220
+ const r = Object.create( proto );
221
+ for (const prop of Object.getOwnPropertyNames( expr )) {
222
+ const pd = Object.getOwnPropertyDescriptor( expr, prop );
223
+ if (!pd.enumerable) { // should include all properties starting with _
224
+ if (!skipUnderscored ||
225
+ prop === '_artifact' || prop === '_navigation' || prop === '_effectiveType')
226
+ Object.defineProperty( r, prop, pd );
227
+ }
228
+ else if (!proto) {
229
+ r[prop] = copyExpr( pd.value, location, skipUnderscored, rewritePath );
230
+ }
231
+ else if (prop === 'location') {
232
+ r[prop] = location || pd.value;
233
+ }
234
+ else if (prop.charAt(0) !== '$' || prop === '$inferred') {
235
+ r[prop] = copyExpr( pd.value, location, skipUnderscored, rewritePath );
236
+ }
237
+ else if (!skipUnderscored) { // skip $ properties
238
+ Object.defineProperty( r, prop, pd );
239
+ }
240
+ }
241
+ return r;
242
+ }
243
+
244
+ function testExpr( expr, pathTest, queryTest ) {
245
+ // TODO: also check path arguments/filters
246
+ if (!expr || typeof expr === 'string') { // parse error or keywords in {xpr:...}
247
+ return false;
248
+ }
249
+ else if (Array.isArray(expr)) {
250
+ return expr.some( e => testExpr( e, pathTest, queryTest ) );
251
+ }
252
+ else if (expr.path) {
253
+ return pathTest( expr );
254
+ }
255
+ else if (expr.query) {
256
+ return queryTest( expr.query );
257
+ }
258
+ else if (expr.op && expr.args) {
259
+ // unnamed args => array
260
+ if (Array.isArray(expr.args))
261
+ return expr.args.some( e => testExpr( e, pathTest, queryTest ) );
262
+ // named args => dictionary
263
+ for (const namedArg of Object.keys(expr.args)) {
264
+ if (testExpr(expr.args[namedArg], pathTest, queryTest))
265
+ return true;
266
+ }
267
+ }
268
+ return false;
269
+ }
270
+
271
+ // Return true if the path `item` with a final type `assoc` has a max target
272
+ // cardinality greater than one - either specified on the path item or assoc type.
273
+ function targetMaxNotOne( assoc, item ) {
274
+ // Semantics of associations without provided cardinality: [*,0..1]
275
+ const cardinality = item.cardinality || assoc.cardinality;
276
+ return cardinality && cardinality.targetMax && cardinality.targetMax.val !== 1;
277
+ }
278
+
279
+ // Query tree post-order traversal - called for everything which contributes to the query
280
+ // i.e. is necessary to calculate the elements of the query
281
+ // except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
282
+ // NOTE: does not run on non-referred sub queries! Consider using ‹main›.$queries instead!
283
+ function traverseQueryPost( query, simpleOnly, callback ) {
284
+ if (!query) // parser error
285
+ return;
286
+ if (!query.op) { // in FROM (not JOIN)
287
+ if (query.query) // subquery
288
+ traverseQueryPost( query.query, simpleOnly, callback );
289
+ return;
290
+ }
291
+ if (simpleOnly) {
292
+ const { from } = query;
293
+ if (!from || from.join) // parse error or join
294
+ return; // ok are: path or simple sub query (!)
295
+ }
296
+ if (query.from) { // SELECT
297
+ traverseQueryPost( query.from, simpleOnly, callback );
298
+ // console.log('FC:')
299
+ callback( query );
300
+ // console.log('FE:')
301
+ }
302
+ else if (query.args) { // JOIN, UNION, INTERSECT
303
+ if (!query.join && simpleOnly == null) {
304
+ // enough for elements: traverse only first args for UNION/INTERSECT
305
+ // TODO: we might use this also when we do not rewrite associations
306
+ // in non-referred sub queries
307
+ traverseQueryPost( query.args[0], simpleOnly, callback );
308
+ }
309
+ else {
310
+ for (const q of query.args)
311
+ traverseQueryPost( q, simpleOnly, callback );
312
+ // The ON condition has to be traversed extra, because it must be evaluated
313
+ // after the complete FROM has been traversed. It is also not necessary to
314
+ // evaluate it in populateQuery().
315
+ }
316
+ }
317
+ // else: with parse error (`select from <EOF>`, `select distinct from;`)
318
+ }
319
+
320
+ // Call callback on all queries in dependency order, i.e. starting with query Q
321
+ // 1. sub queries in FROM sources of Q
322
+ // 2. Q itself, except if non-referred query, but with right UNION parts
323
+ // 3. sub queries in ON in FROM of Q
324
+ // 4. sub queries in columns, WHERE, HAVING
325
+ function traverseQueryExtra( main, callback ) {
326
+ if (!main.$queries)
327
+ return;
328
+ // with a top-level UNION, $queries[0] is just the left
329
+ traverseQueryPost( main.query, false, (q) => { // also with right of UNION (to be compatible)
330
+ setLink( q, '_status', 'extra' );
331
+ callback( q );
332
+ } );
333
+ for (const query of main.$queries.slice(1)) {
334
+ if (query._status === 'extra' || query._parent.kind === '$tableAlias')
335
+ continue; // if parent is alias, query is FROM source -> run by traverseQueryPost
336
+ // we are now in the top-level (parent is entity) or a non-referred query (parent is query)
337
+ setLink( query, '_status', 'extra' ); // do not call callback() in non-referred query
338
+ // console.log( 'A:', query.name,query._status)
339
+ traverseQueryPost( query, null, (q) => {
340
+ if (q._status !== 'extra') {
341
+ // console.log( 'T:', q.name)
342
+ setLink( q, '_status', 'extra' );
343
+ callback( q );
344
+ }
345
+ // else console.log( 'E:', q.name)
346
+ } );
347
+ }
348
+ }
349
+
350
+ // About Helper property $expand for faster the XSN-to-CSN transformation
351
+ // - null/undefined: artifact, member, items does not contain expanded members
352
+ // - 'origin': all expanded (sub) elements have no new target/on and no new annotations
353
+ // that value is only on elements, types, and params -> no other members
354
+ // when set, only on elem/art with expanded elements
355
+ // - 'target': all expanded (sub) elements might only have new target/on, but
356
+ // no indivual annotations on any (sub) member
357
+ // when set, traverse all parents where the value has been 'origin' before
358
+ // - 'annotate': at least one inferred (sub) member has an individual annotation,
359
+ // not counting propagated ones; set up to the definition (main artifact)
360
+ // (only set with anno on $inferred elem), annotate “beats” target
361
+ // Usage according to CSN flavor:
362
+ // - gensrc: do not render inferred elements (including expanded elements),
363
+ // collect annotate statements with value 'annotate'
364
+ // - client: do not render expanded sub elements if artifact/member is no type, has a type,
365
+ // has $expand = 'origin', and all its _origin also have $expand = 'origin'
366
+ // (might sometimes render the elements unnecessarily, which is not wrong)
367
+ // - universal: do not render expanded sub elements if $expand = 'origin'
368
+ function setExpandStatus( elem, status ) {
369
+ // set on element
370
+ while (elem._main) {
371
+ elem = elem._parent;
372
+ if (status === 'annotate' ? elem.$expand === 'annotate' : elem.$expand !== 'origin')
373
+ return;
374
+ elem.$expand = status; // meaning: expanded, containing assocs
375
+ for (let line = elem.items; line; line = line.items)
376
+ line.$expand = status; // to-csn just uses the innermost $expand
377
+ }
378
+ }
221
379
 
222
380
  module.exports = {
223
381
  pushLink,
@@ -225,7 +383,7 @@ module.exports = {
225
383
  annotationIsFalse,
226
384
  annotateWith,
227
385
  setLink,
228
- setProp,
386
+ setArtifactLink,
229
387
  linkToOrigin,
230
388
  dependsOn,
231
389
  dependsOnSilent,
@@ -235,4 +393,10 @@ module.exports = {
235
393
  pathName,
236
394
  augmentPath,
237
395
  splitIntoPath,
396
+ copyExpr,
397
+ testExpr,
398
+ targetMaxNotOne,
399
+ traverseQueryPost,
400
+ traverseQueryExtra,
401
+ setExpandStatus,
238
402
  };
@@ -0,0 +1,5 @@
1
+ {
2
+ "rules": {
3
+ "no-shadow": "off"
4
+ }
5
+ }
@@ -504,7 +504,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
504
504
  let hasAlternativeCarrier = false; // is the alternative annotation target available in the EDM?
505
505
  let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
506
506
 
507
- if (carrier.kind === 'entity' || carrier.kind === 'view') {
507
+ if (carrier.kind === 'entity') {
508
508
  // If AppliesTo=[EntitySet/Singleton/Collection, EntityType], EntitySet/Singleton/Collection has precedence
509
509
  testToAlternativeEdmTarget = (x => {
510
510
  if(options.isV2()) {
@@ -554,7 +554,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
554
554
  }
555
555
  else {
556
556
  // this might be more precise if handleAnnotation would know more about the carrier
557
- testToStandardEdmTarget = (x => x
557
+ testToStandardEdmTarget = (x => x
558
558
  ? ['Parameter', 'Property'].some(y => x.includes(y) ||
559
559
  carrier._isCollection && x.includes('Collection'))
560
560
  : true);
@@ -948,7 +948,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
948
948
  if (typeof val === 'string') {
949
949
  if (dTypeName === 'Edm.Boolean') {
950
950
  typeName = 'Bool';
951
- if (!['true','false'].includes(val)) {
951
+ if (val !== 'true' && val !== 'false') {
952
952
  message(warning, context, `found String, but expected type ${ dTypeName }`);
953
953
  }
954
954
  }
@@ -1284,14 +1284,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
1284
1284
  });
1285
1285
  }
1286
1286
  else { // literal
1287
- let escaped = obj;
1288
- if (typeof escaped === 'string') {
1289
- escaped = escaped.replace(/&/g, '&amp;')
1290
- }
1291
1287
  edmNode = new Edm.ValueThing(v,
1292
- exprDef && exprDef.valueThingName || getXmlTypeName(escaped), escaped);
1288
+ exprDef && exprDef.valueThingName || getXmlTypeName(obj), obj);
1293
1289
  // typename for static expression rendering
1294
- edmNode.setJSON( { [getJsonTypeName(escaped)]: escaped } );
1290
+ edmNode.setJSON( { [getJsonTypeName(obj)]: obj } );
1295
1291
  }
1296
1292
  }
1297
1293
  }
@@ -101,7 +101,7 @@ function preprocessAnnotations(csn, serviceName, options) {
101
101
 
102
102
  // inner functions
103
103
  function draftAnnotations(carrier, aName, aNameWithoutQualifier) {
104
- if ((carrier.kind === 'entity' || carrier.kind === 'view') &&
104
+ if ((carrier.kind === 'entity') &&
105
105
  (aNameWithoutQualifier === '@Common.DraftRoot.PreparationAction' ||
106
106
  aNameWithoutQualifier === '@Common.DraftRoot.ActivationAction' ||
107
107
  aNameWithoutQualifier === '@Common.DraftRoot.EditAction' ||
@@ -136,7 +136,7 @@ function preprocessAnnotations(csn, serviceName, options) {
136
136
  return false;
137
137
  }
138
138
 
139
- if (carrier.kind === 'entity' || carrier.kind === 'view') {
139
+ if (carrier.kind === 'entity') {
140
140
  warning(null, null, `annotation preprocessing/${aNameWithoutQualifier}: annotation must not be used for an entity, ${ctx}`);
141
141
  return false;
142
142
  }
@@ -45,6 +45,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
45
45
  let [ allServices,
46
46
  allSchemas,
47
47
  whatsMyServiceRootName,
48
+ autoexposeSchemaName,
48
49
  options ] = initializeModel(csn, _options, messageFunctions);
49
50
 
50
51
  const Edm = require('./edm.js')(options, error);
@@ -188,9 +189,9 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
188
189
  fqSchemaXRef.sort((a,b) => b.length-a.length);
189
190
 
190
191
  // Bring the schemas in alphabetical order, service first, root last
191
- const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== 'root' && n !== serviceCsn.name).sort();
192
- if(subSchemaDictionary.root)
193
- sortedSchemaNames.push('root');
192
+ const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== autoexposeSchemaName && n !== serviceCsn.name).sort();
193
+ if(subSchemaDictionary[autoexposeSchemaName])
194
+ sortedSchemaNames.push(autoexposeSchemaName);
194
195
 
195
196
  // Finally create the schemas and register them in the service.
196
197
  LeadSchema = createSchema(subSchemaDictionary[serviceCsn.name]);
@@ -234,7 +235,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
234
235
  Object.entries(csn.definitions).forEach(([fqName, art]) => {
235
236
  // Identify service members by their definition name only, this allows
236
237
  // to let the internal object.name have the sub-schema name.
237
- // With nested services we must do a longest path match and check wether
238
+ // With nested services we must do a longest path match and check whether
238
239
  // the current definition belongs to the current toplevel service definition.
239
240
 
240
241
  // Definition is to be considered if
@@ -246,7 +247,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
246
247
  // not marked to be ignored as schema member
247
248
  if(mySchemaName &&
248
249
  serviceCsn.name === whatsMyServiceRootName(fqName, false) &&
249
- ![ 'context', 'service' ].includes(art.kind)) {
250
+ art.kind !== 'context' && art.kind !== 'service') {
250
251
 
251
252
  // Strip the toplevel serviceName from object.name
252
253
  // except if the schema name is the service name itself.
@@ -574,7 +575,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
574
575
  edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
575
576
  const paramLoc = [...actLoc, 'params', parameterName];
576
577
  const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
577
- if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
578
+ if(param._type && !param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
578
579
  warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
579
580
  }
580
581
  if(param._isCollection) {
@@ -591,8 +592,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
591
592
  // it is safe to assume that either type or items.type are set
592
593
  const returns = action.returns.items || action.returns;
593
594
  let type = returns.type;
594
- if(type){
595
- if(!isBuiltinType(type) && !['entity', 'view', 'type'].includes(csn.definitions[type].kind)){
595
+ if (type){
596
+ if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
596
597
  const returnsLoc = [ ...actLoc, 'returns'];
597
598
  warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
598
599
  }
package/lib/edm/edm.js CHANGED
@@ -137,11 +137,11 @@ module.exports = function (options, error) {
137
137
  let tmpStr = '';
138
138
  edmUtils.forAll(this, (v, p) => {
139
139
  if (typeof this[p] !== 'object')
140
- tmpStr += ' ' + p + '="' + edmUtils.escapeString(v) + '"'
140
+ tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
141
141
  });
142
142
  edmUtils.forAll(this._xmlOnlyAttributes, (v,p) => {
143
143
  if (typeof v !== 'object')
144
- tmpStr += ' ' + p + '="' + edmUtils.escapeString(v) + '"'
144
+ tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
145
145
  });
146
146
  return tmpStr;
147
147
  }
@@ -599,7 +599,7 @@ module.exports = function (options, error) {
599
599
  // Complex/EntityType are derived from TypeBase
600
600
  // but have no type attribute in their CSN
601
601
  if(typecsn.type) { // this thing has a type
602
- // check wether this is a scalar type (or array of scalar type) or a named type
602
+ // check whether this is a scalar type (or array of scalar type) or a named type
603
603
  let scalarType = undefined;
604
604
  if(typecsn.items && typecsn.items.type &&
605
605
  isBuiltinType(typecsn.items.type)) {
@@ -879,11 +879,12 @@ module.exports = function (options, error) {
879
879
  // the V2 metadata.xml
880
880
  // @ts-ignore
881
881
  Property.SAP_Annotation_Attribute_WhiteList = [
882
- '@sap.hierarchy.node.for', //-> sap:hierarchy-node-for
883
- '@sap.hierarchy.parent.node.for', // -> sap:hierarchy-parent-node-for
884
- '@sap.hierarchy.level.for', // -> sap:hierarchy-level-for
885
- '@sap.hierarchy.drill.state.for', // -> sap:hierarchy-drill-state-for
886
- '@sap.hierarchy.node.descendant.count.for' // -> sap:hierarchy-node-descendant-count-for
882
+ '@sap.hierarchy.node.for', // -> sap:hierarchy-node-for
883
+ '@sap.hierarchy.parent.node.for', // -> sap:hierarchy-parent-node-for
884
+ '@sap.hierarchy.level.for', // -> sap:hierarchy-level-for
885
+ '@sap.hierarchy.drill.state.for', // -> sap:hierarchy-drill-state-for
886
+ '@sap.hierarchy.node.descendant.count.for', // -> sap:hierarchy-node-descendant-count-for
887
+ '@sap.parameter'
887
888
  ];
888
889
 
889
890
  super(v, attributes, csn);
@@ -927,9 +928,7 @@ module.exports = function (options, error) {
927
928
  Additionally: The attribute is named 'Default' in V2 and 'DefaultValue' in V4
928
929
  */
929
930
  if(this.v4)
930
- this[`Default${this.v4 ? 'Value' : ''}`] = ['cds.Boolean', 'cds.Binary', 'cds.LargeBinary', 'cds.Integer64', 'cds.Integer'].includes(csn.type)
931
- ? defVal
932
- : edmUtils.escapeString(defVal);
931
+ this[`Default${this.v4 ? 'Value' : ''}`] = defVal;
933
932
  }
934
933
  }
935
934
  }
@@ -1356,7 +1355,7 @@ module.exports = function (options, error) {
1356
1355
  {
1357
1356
  let kind = this.kind;
1358
1357
  let xml = indent + '<' + kind + this.toXMLattributes();
1359
- xml += (this._value !== undefined ? '>' + edmUtils.escapeString(this._value) + '</' + kind + '>' : '/>');
1358
+ xml += (this._value !== undefined ? '>' + edmUtils.escapeStringForText(this._value) + '</' + kind + '>' : '/>');
1360
1359
  return xml;
1361
1360
  }
1362
1361