@sap/cds-compiler 4.7.6 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +63 -3
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +28 -1
  6. package/bin/cdshi.js +13 -3
  7. package/doc/CHANGELOG_BETA.md +24 -1
  8. package/lib/api/main.js +119 -46
  9. package/lib/api/options.js +51 -0
  10. package/lib/api/validate.js +1 -5
  11. package/lib/base/builtins.js +116 -0
  12. package/lib/base/keywords.js +5 -1
  13. package/lib/base/location.js +91 -14
  14. package/lib/base/message-registry.js +76 -46
  15. package/lib/base/messages.js +121 -35
  16. package/lib/base/model.js +4 -7
  17. package/lib/checks/actionsFunctions.js +3 -3
  18. package/lib/checks/annotationsOData.js +3 -0
  19. package/lib/checks/defaultValues.js +5 -2
  20. package/lib/checks/elements.js +2 -1
  21. package/lib/checks/enricher.js +2 -2
  22. package/lib/checks/queryNoDbArtifacts.js +5 -3
  23. package/lib/checks/utils.js +1 -1
  24. package/lib/checks/validator.js +8 -56
  25. package/lib/compiler/assert-consistency.js +11 -7
  26. package/lib/compiler/builtins.js +0 -74
  27. package/lib/compiler/checks.js +105 -29
  28. package/lib/compiler/define.js +37 -25
  29. package/lib/compiler/extend.js +35 -12
  30. package/lib/compiler/index.js +9 -10
  31. package/lib/compiler/lsp-api.js +5 -0
  32. package/lib/compiler/populate.js +13 -5
  33. package/lib/compiler/propagator.js +24 -18
  34. package/lib/compiler/resolve.js +47 -45
  35. package/lib/compiler/shared.js +61 -21
  36. package/lib/compiler/tweak-assocs.js +15 -90
  37. package/lib/compiler/utils.js +3 -3
  38. package/lib/compiler/xpr-rewrite.js +689 -0
  39. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  40. package/lib/edm/annotations/edmJson.js +7 -5
  41. package/lib/edm/annotations/genericTranslation.js +149 -71
  42. package/lib/edm/csn2edm.js +25 -9
  43. package/lib/edm/edm.js +7 -7
  44. package/lib/edm/edmInboundChecks.js +57 -5
  45. package/lib/edm/edmPreprocessor.js +54 -25
  46. package/lib/edm/edmUtils.js +3 -16
  47. package/lib/gen/Dictionary.json +138 -14
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +1 -1
  50. package/lib/gen/languageParser.js +2085 -1989
  51. package/lib/json/csnVersion.js +7 -4
  52. package/lib/json/from-csn.js +21 -11
  53. package/lib/json/to-csn.js +8 -4
  54. package/lib/language/antlrParser.js +1 -1
  55. package/lib/language/genericAntlrParser.js +23 -16
  56. package/lib/language/multiLineStringParser.js +2 -2
  57. package/lib/language/textUtils.js +1 -1
  58. package/lib/main.d.ts +90 -14
  59. package/lib/main.js +9 -1
  60. package/lib/model/cloneCsn.js +21 -9
  61. package/lib/model/csnRefs.js +153 -42
  62. package/lib/model/csnUtils.js +14 -11
  63. package/lib/model/enrichCsn.js +4 -2
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/model/sortViews.js +14 -6
  66. package/lib/modelCompare/compare.js +135 -122
  67. package/lib/optionProcessor.js +49 -2
  68. package/lib/render/DuplicateChecker.js +6 -6
  69. package/lib/render/manageConstraints.js +1 -0
  70. package/lib/render/toCdl.js +6 -3
  71. package/lib/render/toHdbcds.js +4 -48
  72. package/lib/render/toSql.js +6 -3
  73. package/lib/transform/addTenantFields.js +58 -35
  74. package/lib/transform/db/applyTransformations.js +34 -1
  75. package/lib/transform/db/constraints.js +1 -1
  76. package/lib/transform/db/expansion.js +11 -3
  77. package/lib/transform/db/flattening.js +71 -46
  78. package/lib/transform/db/groupByOrderBy.js +2 -2
  79. package/lib/transform/db/temporal.js +6 -3
  80. package/lib/transform/db/transformExists.js +2 -2
  81. package/lib/transform/db/views.js +1 -4
  82. package/lib/transform/effective/annotations.js +194 -0
  83. package/lib/transform/effective/main.js +11 -10
  84. package/lib/transform/effective/misc.js +45 -14
  85. package/lib/transform/effective/types.js +4 -3
  86. package/lib/transform/forOdata.js +29 -12
  87. package/lib/transform/forRelationalDB.js +104 -113
  88. package/lib/transform/localized.js +7 -6
  89. package/lib/transform/odata/flattening.js +228 -107
  90. package/lib/transform/odata/toFinalBaseType.js +10 -26
  91. package/lib/transform/odata/typesExposure.js +41 -25
  92. package/lib/transform/parseExpr.js +4 -7
  93. package/lib/transform/transformUtils.js +50 -43
  94. package/lib/transform/translateAssocsToJoins.js +48 -48
  95. package/lib/transform/universalCsn/coreComputed.js +2 -1
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  97. package/package.json +2 -2
  98. package/share/messages/README.md +4 -0
  99. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  100. package/share/messages/anno-missing-rewrite.md +45 -0
  101. package/share/messages/check-proper-type-of.md +1 -1
  102. package/share/messages/def-duplicate-autoexposed.md +1 -1
  103. package/share/messages/extend-repeated-intralayer.md +3 -16
  104. package/share/messages/extend-unrelated-layer.md +1 -1
  105. package/share/messages/message-explanations.json +2 -0
  106. package/share/messages/redirected-to-ambiguous.md +1 -1
  107. package/share/messages/redirected-to-complex.md +1 -1
  108. package/share/messages/redirected-to-unrelated.md +1 -1
  109. package/share/messages/rewrite-not-supported.md +1 -1
  110. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  111. package/share/messages/type-missing-enum-value.md +59 -0
  112. package/share/messages/wildcard-excluding-one.md +1 -1
  113. package/bin/.eslintrc.json +0 -17
  114. package/lib/api/.eslintrc.json +0 -37
  115. package/lib/checks/.eslintrc.json +0 -31
  116. package/lib/compiler/.eslintrc.json +0 -8
  117. package/lib/edm/.eslintrc.json +0 -46
  118. package/lib/inspect/.eslintrc.json +0 -4
  119. package/lib/json/.eslintrc.json +0 -4
  120. package/lib/language/.eslintrc.json +0 -4
  121. package/lib/model/.eslintrc.json +0 -13
  122. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  123. package/lib/render/.eslintrc.json +0 -22
  124. package/lib/transform/.eslintrc.json +0 -13
  125. package/lib/transform/db/.eslintrc.json +0 -41
  126. package/lib/transform/draft/.eslintrc.json +0 -4
  127. package/lib/transform/effective/.eslintrc.json +0 -4
  128. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  129. package/lib/utils/.eslintrc.json +0 -7
package/lib/api/main.js CHANGED
@@ -9,6 +9,8 @@ const messages = lazyload('../base/messages');
9
9
  const compiler = lazyload('../compiler/index');
10
10
  const toCsn = lazyload('../json/to-csn');
11
11
  const forOdataNew = lazyload('../transform/forOdata.js');
12
+ const generateDrafts = lazyload('../transform/draft/odata');
13
+ const tenant = lazyload('../transform/addTenantFields');
12
14
  const toSql = lazyload('../render/toSql');
13
15
  const toCdl = require('../render/toCdl');
14
16
  const modelCompare = lazyload('../modelCompare/compare');
@@ -24,10 +26,7 @@ const baseError = lazyload('../base/error');
24
26
  const csnToEdm = lazyload('../edm/csn2edm');
25
27
  const trace = lazyload('./trace');
26
28
  const cloneCsn = lazyload('../model/cloneCsn');
27
-
28
- const { forEach, forEachKey } = require('../utils/objectUtils');
29
- const { makeMessageFunction } = require('../base/messages');
30
- const { sortCsnForTests } = require('../model/cloneCsn');
29
+ const objectUtils = lazyload('../utils/objectUtils');
31
30
 
32
31
  /**
33
32
  * Return the artifact name for use for the hdbresult object
@@ -57,18 +56,18 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
57
56
  function attachTransformerCharacteristics( csn, transformation, options,
58
57
  relevantOptionNames, optionalOptionNames = [] ) {
59
58
  const relevant = {};
60
- for (const name of relevantOptionNames ) {
59
+ for (const name of relevantOptionNames) {
61
60
  if (options[name] !== undefined)
62
61
  relevant[name] = options[name];
63
62
  }
64
63
 
65
- for (const name of optionalOptionNames ) {
64
+ for (const name of optionalOptionNames) {
66
65
  if (options[name] !== undefined)
67
66
  relevant[name] = options[name];
68
67
  }
69
68
 
70
69
  // eslint-disable-next-line sonarjs/no-empty-collection
71
- for (const name of relevantGeneralOptions ) {
70
+ for (const name of relevantGeneralOptions) {
72
71
  if (options[name] !== undefined)
73
72
  relevant[name] = options[name];
74
73
  }
@@ -140,7 +139,7 @@ function isPreTransformed( csn, transformation ) {
140
139
  function odataInternal( csn, internalOptions, messageFunctions ) {
141
140
  internalOptions.transformation = 'odata';
142
141
  let oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions, messageFunctions);
143
- oDataCsn = sortCsnForTests(oDataCsn, internalOptions);
142
+ oDataCsn = cloneCsn.sortCsnForTests(oDataCsn, internalOptions);
144
143
  messageFunctions.setModel(oDataCsn);
145
144
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
146
145
  return oDataCsn;
@@ -155,11 +154,32 @@ function odataInternal( csn, internalOptions, messageFunctions ) {
155
154
  * @returns {oDataCSN} Return an oData-pre-processed CSN
156
155
  */
157
156
  function odata( csn, options, messageFunctions ) {
158
- trace.traceApi('for.odata', options);
159
157
  const internalOptions = prepareOptions.for.odata(options);
158
+ messageFunctions.setOptions( internalOptions );
160
159
  return odataInternal(csn, internalOptions, messageFunctions);
161
160
  }
162
161
 
162
+ /**
163
+ * Return a structured CSN for the Java Runtime: with drafts and tenant support
164
+ *
165
+ * @param {CSN.Model} csn Clean input CSN
166
+ * @param {ODataOptions} options Options
167
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
168
+ * @returns {CSN.Model} a CSN for the Java Runtime
169
+ */
170
+ function java( csn, options, messageFunctions ) {
171
+ const internalOptions = prepareOptions.for.java(options);
172
+ internalOptions.transformation = 'odata'; // otherwise generateDrafts adds tenant
173
+ messageFunctions.setOptions( internalOptions );
174
+ handleTenantDiscriminator(options, internalOptions, messageFunctions);
175
+
176
+ const clone = cloneCsn.cloneFullCsn(csn, internalOptions);
177
+ const draft = generateDrafts(clone, internalOptions, undefined, messageFunctions);
178
+ if (internalOptions.tenantDiscriminator)
179
+ tenant.addTenantFields(draft, internalOptions, messageFunctions );
180
+ return draft;
181
+ }
182
+
163
183
  /**
164
184
  * Process the given csn back to cdl.
165
185
  *
@@ -169,8 +189,8 @@ function odata( csn, options, messageFunctions ) {
169
189
  * @returns {object} { model: string, namespace: string }
170
190
  */
171
191
  function cdl( csn, options, messageFunctions ) {
172
- trace.traceApi('to.cdl', options);
173
192
  const internalOptions = prepareOptions.to.cdl(options);
193
+ messageFunctions.setOptions( internalOptions );
174
194
  return toCdl.csnToCdl(csn, internalOptions, messageFunctions);
175
195
  }
176
196
 
@@ -204,6 +224,7 @@ function csnForSql( csn, internalOptions, messageFunctions ) {
204
224
  */
205
225
  function forSql( csn, options, messageFunctions ) {
206
226
  const internalOptions = prepareOptions.to.sql(options);
227
+ messageFunctions.setOptions( internalOptions );
207
228
  return csnForSql(csn, internalOptions, messageFunctions); // already sorted for test mode
208
229
  }
209
230
 
@@ -219,6 +240,7 @@ function forSql( csn, options, messageFunctions ) {
219
240
  function forHdi( csn, options, messageFunctions ) {
220
241
  const internalOptions = prepareOptions.to.hdi(options);
221
242
  internalOptions.transformation = 'sql';
243
+ messageFunctions.setOptions( internalOptions );
222
244
  const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(
223
245
  csn, internalOptions, messageFunctions
224
246
  );
@@ -236,6 +258,7 @@ function forHdi( csn, options, messageFunctions ) {
236
258
  function forHdbcds( csn, options, messageFunctions ) {
237
259
  const internalOptions = prepareOptions.to.hdbcds(options);
238
260
  internalOptions.transformation = 'hdbcds';
261
+ messageFunctions.setOptions( internalOptions );
239
262
  const hanaCsn = forRelationalDB.transformForRelationalDBWithCsn(
240
263
  csn, internalOptions, messageFunctions
241
264
  );
@@ -247,13 +270,13 @@ function forHdbcds( csn, options, messageFunctions ) {
247
270
  *
248
271
  * @param {CSN.Model} csn Plain input CSN
249
272
  * @param {EffectiveCsnOptions} options Options
273
+ * @param {EffectiveCsnOptions} internalOptions Options that were already processed
250
274
  * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
251
275
  * @returns {CSN.Model} CSN transformed
252
276
  * @private
253
277
  */
254
- function forEffective( csn, options, messageFunctions ) {
255
- const internalOptions = prepareOptions.to.sql(options);
256
- internalOptions.transformation = 'effective';
278
+ function forEffectiveInternal( csn, options, internalOptions, messageFunctions ) {
279
+ messageFunctions.setOptions( internalOptions );
257
280
  if (options.tenantDiscriminator) {
258
281
  messageFunctions.error('api-invalid-option', null, {
259
282
  '#': 'forbidden',
@@ -267,6 +290,40 @@ function forEffective( csn, options, messageFunctions ) {
267
290
  return cloneCsn.sortCsnForTests(eCsn, internalOptions);
268
291
  }
269
292
 
293
+ /**
294
+ * SEAL CSN transformation
295
+ *
296
+ * @param {CSN.Model} csn Plain input CSN
297
+ * @param {EffectiveCsnOptions} options Options
298
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
299
+ * @returns {CSN.Model} CSN transformed
300
+ * @private
301
+ */
302
+ function forSeal( csn, options, messageFunctions ) {
303
+ const internalOptions = prepareOptions.for.seal(options);
304
+ internalOptions.transformation = 'effective';
305
+ return forEffectiveInternal(csn, options, internalOptions, messageFunctions);
306
+ }
307
+
308
+ /**
309
+ * Effective CSN transformation
310
+ *
311
+ * @param {CSN.Model} csn Plain input CSN
312
+ * @param {EffectiveCsnOptions} options Options
313
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
314
+ * @returns {CSN.Model} CSN transformed
315
+ * @private
316
+ */
317
+ function forEffective( csn, options, messageFunctions ) {
318
+ const internalOptions = prepareOptions.for.effective(options);
319
+ internalOptions.transformation = 'effective';
320
+ // for.effective is still beta mode
321
+ if (!baseModel.isBetaEnabled(options, 'effectiveCsn'))
322
+ throw new baseError.CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
323
+
324
+ return forEffectiveInternal(csn, options, internalOptions, messageFunctions);
325
+ }
326
+
270
327
  /**
271
328
  * Process the given CSN into SQL.
272
329
  *
@@ -276,9 +333,9 @@ function forEffective( csn, options, messageFunctions ) {
276
333
  * @returns {SQL[]} Array of SQL statements, tables first, views second
277
334
  */
278
335
  function sql( csn, options, messageFunctions ) {
279
- trace.traceApi('to.sql', options);
280
336
  const internalOptions = prepareOptions.to.sql(options);
281
337
  internalOptions.transformation = 'sql';
338
+ messageFunctions.setOptions( internalOptions );
282
339
 
283
340
  handleTenantDiscriminator(options, internalOptions, messageFunctions);
284
341
 
@@ -303,8 +360,8 @@ function sql( csn, options, messageFunctions ) {
303
360
  * @returns {HDIArtifacts} { <filename>:<content>, ...}
304
361
  */
305
362
  function hdi( csn, options, messageFunctions ) {
306
- trace.traceApi('to.hdi', options);
307
363
  const internalOptions = prepareOptions.to.hdi(options);
364
+ messageFunctions.setOptions( internalOptions );
308
365
 
309
366
  handleTenantDiscriminator(options, internalOptions, messageFunctions);
310
367
 
@@ -325,7 +382,7 @@ function hdi( csn, options, messageFunctions ) {
325
382
  const sqlArtifactsWithCSNNamesToSort = Object.create(null);
326
383
  const sqlArtifactsNotToSort = Object.create(null);
327
384
 
328
- forEach(flat, (key) => {
385
+ objectUtils.forEach(flat, (key) => {
329
386
  const artifactNameLikeInCsn = key.replace(/\.[^/.]+$/, '');
330
387
  nameMapping[artifactNameLikeInCsn] = key;
331
388
  if (key.endsWith('.hdbtable') || key.endsWith('.hdbview'))
@@ -343,7 +400,7 @@ function hdi( csn, options, messageFunctions ) {
343
400
  }, Object.create(null));
344
401
 
345
402
  // now add the not-sorted stuff, like indices
346
- forEach(sqlArtifactsNotToSort, (key) => {
403
+ objectUtils.forEach(sqlArtifactsNotToSort, (key) => {
347
404
  sorted[remapName(key, sqlCSN, k => !k.endsWith('.hdbindex'))] = sqlArtifactsNotToSort[key];
348
405
  });
349
406
 
@@ -365,7 +422,7 @@ function hdi( csn, options, messageFunctions ) {
365
422
  function remapNames( dict, csn, filter ) {
366
423
  const result = Object.create(null);
367
424
 
368
- forEach(dict, (key, value) => {
425
+ objectUtils.forEach(dict, (key, value) => {
369
426
  const name = remapName(key, csn, filter);
370
427
  result[name] = value;
371
428
  });
@@ -410,8 +467,8 @@ function remapName( key, csn, filter = () => true ) {
410
467
  * - createsAndAlters: An array of SQL statements to ALTER/CREATE tables/views
411
468
  */
412
469
  function sqlMigration( csn, options, messageFunctions, beforeImage ) {
413
- trace.traceApi('to.sql.migration', options);
414
470
  const internalOptions = prepareOptions.to.sql(options);
471
+ messageFunctions.setOptions( internalOptions );
415
472
  handleTenantDiscriminator(options, internalOptions, messageFunctions);
416
473
  const { error, throwWithError } = messageFunctions;
417
474
 
@@ -513,7 +570,7 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
513
570
  const createAndAlterSqls = [];
514
571
  // Turn the structured result into just a flat dictionary of "artifact name": "sql"
515
572
  const flatSqlDict = Object.values(hdbkinds).reduce((prev, curr) => {
516
- forEach(curr, (name, value) => {
573
+ objectUtils.forEach(curr, (name, value) => {
517
574
  prev[name] = value;
518
575
  });
519
576
  return prev;
@@ -563,8 +620,8 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
563
620
  * @returns {migration} The migration result
564
621
  */
565
622
  function hdiMigration( csn, options, messageFunctions, beforeImage ) {
566
- trace.traceApi('to.hdi.migration', options);
567
623
  const internalOptions = prepareOptions.to.hdi(options);
624
+ messageFunctions.setOptions( internalOptions );
568
625
  handleTenantDiscriminator(options, internalOptions, messageFunctions);
569
626
 
570
627
  // Prepare after-image.
@@ -602,9 +659,9 @@ function hdiMigration( csn, options, messageFunctions, beforeImage ) {
602
659
  */
603
660
  function createSqlDefinitions( hdbkinds, afterImage ) {
604
661
  const result = [];
605
- forEach(hdbkinds, (kind, artifacts) => {
662
+ objectUtils.forEach(hdbkinds, (kind, artifacts) => {
606
663
  const suffix = `.${ kind }`;
607
- forEach(artifacts, (name, sqlStatement) => {
664
+ objectUtils.forEach(artifacts, (name, sqlStatement) => {
608
665
  if ( kind !== 'hdbindex' )
609
666
  result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
610
667
  else
@@ -622,7 +679,7 @@ function createSqlDefinitions( hdbkinds, afterImage ) {
622
679
  */
623
680
  function createSqlDeletions( deletions, beforeImage ) {
624
681
  const result = [];
625
- forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
682
+ objectUtils.forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
626
683
  return result;
627
684
  }
628
685
  /**
@@ -634,7 +691,7 @@ function createSqlDeletions( deletions, beforeImage ) {
634
691
  */
635
692
  function createSqlMigrations( migrations, afterImage ) {
636
693
  const result = [];
637
- forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
694
+ objectUtils.forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
638
695
  return result;
639
696
  }
640
697
 
@@ -651,9 +708,13 @@ sql.migration = sqlMigration;
651
708
  * @returns {HDBCDS} { <filename>:<content>, ...}
652
709
  */
653
710
  function hdbcds( csn, options, messageFunctions ) {
654
- trace.traceApi('to.hdbcds', options);
655
711
  const internalOptions = prepareOptions.to.hdbcds(options);
656
712
  internalOptions.transformation = 'hdbcds';
713
+ messageFunctions.setOptions( internalOptions );
714
+
715
+ // no "isBetaEnabled", because this warning must also appear with "deprecated" flags
716
+ if (internalOptions.betaMode || internalOptions.beta?.v5preview)
717
+ messageFunctions.warning('api-deprecated-v5', null, null);
657
718
 
658
719
  if (options.tenantDiscriminator) {
659
720
  messageFunctions.error('api-invalid-option', null, {
@@ -678,12 +739,12 @@ function hdbcds( csn, options, messageFunctions ) {
678
739
  * @returns {edm} The JSON representation of the service
679
740
  */
680
741
  function edm( csn, options, messageFunctions ) {
681
- trace.traceApi('to.edm', options);
682
742
  // If not provided at all, set service to 'undefined' to trigger validation
683
743
  const internalOptions = prepareOptions.to.edm(
684
744
  // eslint-disable-next-line comma-dangle
685
745
  options.service ? options : Object.assign({ service: undefined }, options)
686
746
  );
747
+ messageFunctions.setOptions( internalOptions );
687
748
 
688
749
  const { service } = options;
689
750
 
@@ -712,8 +773,8 @@ edm.all = edmall;
712
773
  * @returns {edms} { <service>:<JSON representation>, ...}
713
774
  */
714
775
  function edmall( csn, options, messageFunctions ) {
715
- trace.traceApi('to.edm.all', options);
716
776
  const internalOptions = prepareOptions.to.edm(options);
777
+ messageFunctions.setOptions( internalOptions );
717
778
  const { error } = messageFunctions;
718
779
 
719
780
  if (internalOptions.odataVersion === 'v2')
@@ -747,12 +808,12 @@ function edmall( csn, options, messageFunctions ) {
747
808
  * @returns {edmx} The XML representation of the service
748
809
  */
749
810
  function edmx( csn, options, messageFunctions ) {
750
- trace.traceApi('to.edmx', options);
751
811
  // If not provided at all, set service to 'undefined' to trigger validation
752
812
  const internalOptions = prepareOptions.to.edmx(
753
813
  // eslint-disable-next-line comma-dangle
754
814
  options.service ? options : Object.assign({ service: undefined }, options)
755
815
  );
816
+ messageFunctions.setOptions( internalOptions );
756
817
 
757
818
  const { service } = options;
758
819
 
@@ -782,8 +843,8 @@ edmx.all = edmxall;
782
843
  * @returns {edmxs} { <service>:<XML representation>, ...}
783
844
  */
784
845
  function edmxall( csn, options, messageFunctions ) {
785
- trace.traceApi('to.edmx.all', options);
786
846
  const internalOptions = prepareOptions.to.edmx(options);
847
+ messageFunctions.setOptions( internalOptions );
787
848
 
788
849
  const result = {};
789
850
  let oDataCsn = csn;
@@ -813,18 +874,18 @@ function edmxall( csn, options, messageFunctions ) {
813
874
  * If odataVersion is not 'v4', then no JSON is rendered
814
875
  *
815
876
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
816
- * @param {ODataOptions} [options={}] Options
877
+ * @param {ODataOptions} options Options
817
878
  * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
818
879
  * @returns {object} { <protocol> : { <ServiceName>: { edmx: <XML representation>, edm: <JSON representation> } } }
819
880
  */
820
881
  // @ts-ignore
821
- function odata2( csn, options = {}, messageFunctions ) {
822
- trace.traceApi('to.odata', options);
882
+ function odata2( csn, options, messageFunctions ) {
823
883
  // If not provided at all, set service to undefined to trigger validation
824
884
  const internalOptions = prepareOptions.to.odata(
825
885
  // eslint-disable-next-line comma-dangle
826
886
  options.service ? options : Object.assign({ service: undefined }, options)
827
887
  );
888
+ messageFunctions.setOptions( internalOptions );
828
889
 
829
890
  const { service } = options;
830
891
 
@@ -859,13 +920,13 @@ odata2.all = odataall;
859
920
  * If odataVersion is not 'v4', then no JSON is rendered
860
921
  *
861
922
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
862
- * @param {ODataOptions} [options={}] Options
923
+ * @param {ODataOptions} options Options
863
924
  * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
864
925
  * @returns {object} { <protocol>: { <serviceName>: { edmx: <XML representation>, edm: <JSON representation> } } }
865
926
  */
866
- function odataall( csn, options = {}, messageFunctions ) {
867
- trace.traceApi('to.odata.all', options);
927
+ function odataall( csn, options, messageFunctions ) {
868
928
  const internalOptions = prepareOptions.to.odata(options);
929
+ messageFunctions.setOptions( internalOptions );
869
930
  const { error } = messageFunctions;
870
931
 
871
932
  if (internalOptions.odataVersion === 'v2')
@@ -985,10 +1046,10 @@ function preparedCsnToEdmAll( csn, options, messageFunctions ) {
985
1046
  */
986
1047
  function flattenResultStructure( toProcess ) {
987
1048
  const result = {};
988
- forEach(toProcess, (fileType, artifacts) => {
1049
+ objectUtils.forEach(toProcess, (fileType, artifacts) => {
989
1050
  if (fileType === 'messages')
990
1051
  return;
991
- forEach(artifacts, (filename) => {
1052
+ objectUtils.forEach(artifacts, (filename) => {
992
1053
  result[`${ filename }.${ fileType }`] = artifacts[filename];
993
1054
  });
994
1055
  });
@@ -999,6 +1060,7 @@ function flattenResultStructure( toProcess ) {
999
1060
 
1000
1061
  module.exports = {
1001
1062
  odata: publishCsnProcessor(odata, 'for.odata'),
1063
+ java: publishCsnProcessor(java, 'for.java'),
1002
1064
  cdl: publishCsnProcessor(cdl, 'to.cdl'),
1003
1065
  sql: publishCsnProcessor(sql, 'to.sql'),
1004
1066
  hdi: publishCsnProcessor(hdi, 'to.hdi'),
@@ -1011,14 +1073,15 @@ module.exports = {
1011
1073
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
1012
1074
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
1013
1075
  for_effective: publishCsnProcessor(forEffective, 'for.effective'),
1076
+ for_seal: publishCsnProcessor(forSeal, 'for.seal'),
1014
1077
 
1015
1078
  /* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
1016
1079
  preparedCsnToEdmx(csn, service, options) {
1017
- preparedCsnToEdmx(csn, service, options, makeMessageFunction( csn, options, 'to.edmx' ));
1080
+ preparedCsnToEdmx(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edmx' ));
1018
1081
  },
1019
1082
  /* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
1020
1083
  preparedCsnToEdm(csn, service, options) {
1021
- preparedCsnToEdm(csn, service, options, makeMessageFunction( csn, options, 'to.edm' ));
1084
+ preparedCsnToEdm(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edm' ));
1022
1085
  },
1023
1086
  };
1024
1087
 
@@ -1048,6 +1111,7 @@ function publishCsnProcessor( processor, _name ) {
1048
1111
  * @returns {any} What ever the processor returns
1049
1112
  */
1050
1113
  function api( csn, options = {}, ...args ) {
1114
+ trace.traceApi(_name, options);
1051
1115
  const originalMessageLength = options.messages?.length;
1052
1116
  try {
1053
1117
  const messageFunctions = messages.makeMessageFunction(csn, options, _name);
@@ -1086,7 +1150,6 @@ function publishCsnProcessor( processor, _name ) {
1086
1150
  if (options.testMode) // Attach recompilation reason in testMode
1087
1151
  recompileMsg.message += `\n ↳ cause: ${ err.message }`;
1088
1152
 
1089
- // next line to be replaced by CSN parser call which reads the CSN object
1090
1153
  const xsn = compiler.recompileX(csn, options);
1091
1154
  const recompiledCsn = toCsn.compactModel(xsn);
1092
1155
  messageFunctions.setModel(recompiledCsn);
@@ -1132,7 +1195,7 @@ function checkOutdatedOptions( options, messageFunctions ) {
1132
1195
  });
1133
1196
  }
1134
1197
 
1135
- forEachKey(options.variableReplacements || {}, (name) => {
1198
+ objectUtils.forEachKey(options.variableReplacements || {}, (name) => {
1136
1199
  if (!name.startsWith('$') && name !== 'user' && name !== 'locale') {
1137
1200
  messageFunctions.error('api-invalid-variable-replacement', null, {
1138
1201
  '#': 'noDollar', option: 'variableReplacements', code: '$', name,
@@ -1220,7 +1283,8 @@ function lazyload( moduleName ) {
1220
1283
  }
1221
1284
 
1222
1285
  /**
1223
- * Error when tenantDiscriminator and withHanaAssociations is set by the user.
1286
+ * Error when tenantDiscriminator and withHanaAssociations is set by the user, or
1287
+ * if tenantDiscriminator is used with anything but "plain" mode.
1224
1288
  *
1225
1289
  * Set withHanaAssociations to false when tenantDiscriminator is used.
1226
1290
  *
@@ -1234,12 +1298,21 @@ function handleTenantDiscriminator( options, internalOptions, messageFunctions )
1234
1298
  option: 'tenantDiscriminator',
1235
1299
  prop: 'withHanaAssociations',
1236
1300
  });
1301
+ }
1237
1302
 
1238
- messageFunctions.throwWithAnyError();
1303
+ if (options.tenantDiscriminator && internalOptions.sqlMapping !== 'plain') {
1304
+ messageFunctions.error('api-invalid-combination', null, {
1305
+ '#': 'tenant-and-naming',
1306
+ option: 'tenantDiscriminator',
1307
+ prop: internalOptions.sqlMapping,
1308
+ value: 'plain',
1309
+ });
1239
1310
  }
1240
- else if (internalOptions.tenantDiscriminator) {
1311
+
1312
+ messageFunctions.throwWithError();
1313
+
1314
+ if (internalOptions.tenantDiscriminator)
1241
1315
  internalOptions.withHanaAssociations = false;
1242
- }
1243
1316
  }
1244
1317
 
1245
1318
 
@@ -35,6 +35,7 @@ const publicOptionsNewAPI = [
35
35
  'withHanaAssociations',
36
36
  // ODATA
37
37
  'odataOpenapiHints',
38
+ 'edm4OpenAPI',
38
39
  'odataVersion',
39
40
  'odataFormat',
40
41
  'odataContainment',
@@ -48,6 +49,11 @@ const publicOptionsNewAPI = [
48
49
  'serviceNames',
49
50
  //
50
51
  'dictionaryPrototype',
52
+ // for.effective
53
+ 'resolveSimpleTypes',
54
+ 'resolveProjections',
55
+ 'remapOdataAnnotations',
56
+ 'keepLocalized',
51
57
  ];
52
58
 
53
59
  // Internal options used for testing/debugging etc.
@@ -119,6 +125,8 @@ function translateOptions( input = {}, defaults = {}, hardRequire = {},
119
125
  // Overwrite with the hardRequire options - like src: sql in to.sql()
120
126
  Object.assign(options, hardRequire);
121
127
 
128
+ reclassifyErrorsForOpenApi( options );
129
+
122
130
  // Convenience for $user -> $user.id replacement
123
131
  if (options.variableReplacements && options.variableReplacements.$user && typeof options.variableReplacements.$user === 'string')
124
132
  options.variableReplacements.$user = { id: options.variableReplacements.$user };
@@ -126,6 +134,30 @@ function translateOptions( input = {}, defaults = {}, hardRequire = {},
126
134
  return options;
127
135
  }
128
136
 
137
+ /**
138
+ * Reclassify certain OData errors to warnings for the OData/EDM/EDMX backends.
139
+ * Some errors are not necessary for openAPI generation.
140
+ *
141
+ * @param {CSN.Options} options OData options
142
+ */
143
+ function reclassifyErrorsForOpenApi( options ) {
144
+ if (options.edm4OpenAPI) {
145
+ // shallow clone, so that we can modify severities without changing the user's.
146
+ options.severities = Object.assign({}, options.severities ?? {});
147
+
148
+ options.severities['odata-spec-violation-array'] = 'Warning';
149
+ options.severities['odata-spec-violation-assoc'] = 'Warning';
150
+ options.severities['odata-spec-violation-namespace'] = 'Warning';
151
+ options.severities['odata-spec-violation-param'] = 'Warning';
152
+ options.severities['odata-spec-violation-returns'] = 'Warning';
153
+ options.severities['odata-spec-violation-type-unknown'] = 'Warning';
154
+ options.severities['odata-spec-violation-no-key'] = 'Warning';
155
+ options.severities['odata-spec-violation-key-type'] = 'Warning';
156
+ options.severities['odata-spec-violation-property-name'] = 'Warning';
157
+ }
158
+ }
159
+
160
+
129
161
  module.exports = {
130
162
  to: {
131
163
  cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
@@ -181,6 +213,25 @@ module.exports = {
181
213
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
182
214
  return translateOptions(options, defaultOptions, hardOptions, undefined, undefined, 'for.hana');
183
215
  },
216
+ effective: (options) => {
217
+ const hardOptions = {};
218
+ const defaultOptions = {
219
+ sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, remapOdataAnnotations: false, keepLocalized: false,
220
+ };
221
+ const processed = translateOptions(options, defaultOptions, hardOptions, null, [ 'sql-dialect-and-naming' ], 'for.effective');
222
+
223
+ return Object.assign({}, processed);
224
+ },
225
+ seal: (options) => {
226
+ const hardOptions = {
227
+ sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, keepLocalized: false,
228
+ };
229
+ const defaultOptions = { remapOdataAnnotations: true };
230
+ const processed = translateOptions(options, defaultOptions, hardOptions, null, [ 'sql-dialect-and-naming' ], 'for.effective');
231
+
232
+ return Object.assign({}, processed);
233
+ },
234
+ java: options => translateOptions(options, { sqlMapping: 'plain' }, {}, undefined, undefined, 'for.java'),
184
235
  },
185
236
  overallOptions, // exported for testing
186
237
  };
@@ -140,17 +140,13 @@ const allCombinationValidators = {
140
140
  if (options.sqlDialect && options.sqlMapping && options.sqlDialect !== 'hana' && [ 'quoted', 'hdbcds' ].includes(options.sqlMapping))
141
141
  message.error('api-invalid-combination', null, { '#': 'sql-dialect-and-naming', name: options.sqlDialect, prop: options.sqlMapping });
142
142
  },
143
- 'sql-dialect-and-localized': (options, message) => {
144
- if (options.fewerLocalizedViews && options.sqlDialect === 'hana' && (options.withHanaAssociations || options.withHanaAssociations === undefined))
145
- message.error('api-invalid-combination', null, { '#': 'sql-dialect-and-localized', option: 'fewerLocalizedViews', value: 'hana' });
146
- },
147
143
  'beta-no-test': (options, message) => {
148
144
  if (options.beta && !options.testMode)
149
145
  message.warning('api-unexpected-combination', null, { '#': 'beta-no-test', option: 'beta' });
150
146
  },
151
147
  };
152
148
 
153
- const alwaysRunValidators = [ 'beta-no-test', 'sql-dialect-and-localized' ];
149
+ const alwaysRunValidators = [ 'beta-no-test' ];
154
150
 
155
151
  /**
156
152
  * Run the validations for each option.