@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.
- package/CHANGELOG.md +63 -3
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +28 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +24 -1
- package/lib/api/main.js +119 -46
- package/lib/api/options.js +51 -0
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +116 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +76 -46
- package/lib/base/messages.js +121 -35
- package/lib/base/model.js +4 -7
- package/lib/checks/actionsFunctions.js +3 -3
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +5 -3
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +8 -56
- package/lib/compiler/assert-consistency.js +11 -7
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +105 -29
- package/lib/compiler/define.js +37 -25
- package/lib/compiler/extend.js +35 -12
- package/lib/compiler/index.js +9 -10
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +24 -18
- package/lib/compiler/resolve.js +47 -45
- package/lib/compiler/shared.js +61 -21
- package/lib/compiler/tweak-assocs.js +15 -90
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +149 -71
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +7 -7
- package/lib/edm/edmInboundChecks.js +57 -5
- package/lib/edm/edmPreprocessor.js +54 -25
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +138 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2085 -1989
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +21 -11
- package/lib/json/to-csn.js +8 -4
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +23 -16
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +90 -14
- package/lib/main.js +9 -1
- package/lib/model/cloneCsn.js +21 -9
- package/lib/model/csnRefs.js +153 -42
- package/lib/model/csnUtils.js +14 -11
- package/lib/model/enrichCsn.js +4 -2
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +135 -122
- package/lib/optionProcessor.js +49 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +6 -3
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +34 -1
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +11 -3
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +11 -10
- package/lib/transform/effective/misc.js +45 -14
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +29 -12
- package/lib/transform/forRelationalDB.js +104 -113
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +228 -107
- package/lib/transform/odata/toFinalBaseType.js +10 -26
- package/lib/transform/odata/typesExposure.js +41 -25
- package/lib/transform/parseExpr.js +4 -7
- package/lib/transform/transformUtils.js +50 -43
- package/lib/transform/translateAssocsToJoins.js +48 -48
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- 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
|
|
255
|
-
|
|
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}
|
|
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
|
|
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}
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1311
|
+
|
|
1312
|
+
messageFunctions.throwWithError();
|
|
1313
|
+
|
|
1314
|
+
if (internalOptions.tenantDiscriminator)
|
|
1241
1315
|
internalOptions.withHanaAssociations = false;
|
|
1242
|
-
}
|
|
1243
1316
|
}
|
|
1244
1317
|
|
|
1245
1318
|
|
package/lib/api/options.js
CHANGED
|
@@ -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
|
};
|
package/lib/api/validate.js
CHANGED
|
@@ -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'
|
|
149
|
+
const alwaysRunValidators = [ 'beta-no-test' ];
|
|
154
150
|
|
|
155
151
|
/**
|
|
156
152
|
* Run the validations for each option.
|