@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/api/main.js
CHANGED
|
@@ -4,15 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
const prepareOptions = require('./options');
|
|
6
6
|
const backends = require('../backends');
|
|
7
|
-
const { setProp
|
|
7
|
+
const { setProp } = require('../base/model');
|
|
8
8
|
const { emptyLocation } = require('../base/location');
|
|
9
|
-
const { CompilationError, makeMessageFunction
|
|
10
|
-
const {
|
|
9
|
+
const { CompilationError, makeMessageFunction } = require('../base/messages');
|
|
10
|
+
const { recompileX } = require('../compiler/index');
|
|
11
|
+
const { compactModel, sortCsn } = require('../json/to-csn');
|
|
11
12
|
const { transform4odataWithCsn } = require('../transform/forOdataNew.js');
|
|
12
13
|
const { toSqlDdl } = require('../render/toSql');
|
|
13
14
|
const { compareModels } = require('../modelCompare/compare');
|
|
14
15
|
const sortViews = require('../model/sortViews');
|
|
15
16
|
const { getResultingName } = require('../model/csnUtils');
|
|
17
|
+
const { timetrace } = require('../utils/timetrace');
|
|
18
|
+
const { transformForHanaWithCsn } = require('../transform/forHanaNew');
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Return the artifact name for use for the hdbresult object
|
|
@@ -31,10 +34,10 @@ const propertyToCheck = {
|
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
const { cloneCsn } = require('../model/csnUtils');
|
|
37
|
+
const { toHdbcdsSource } = require('../render/toHdbcds');
|
|
34
38
|
|
|
35
39
|
const relevantGeneralOptions = [ /* for future generic options */ ];
|
|
36
40
|
const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
|
|
37
|
-
const relevantSqlOptions = [ 'sqlMapping', 'sqlDialect' ];
|
|
38
41
|
const warnAboutMismatchOdata = [ 'odataVersion' ];
|
|
39
42
|
|
|
40
43
|
/**
|
|
@@ -132,7 +135,7 @@ function odataInternal(csn, internalOptions) {
|
|
|
132
135
|
* Return a odata-transformed CSN
|
|
133
136
|
*
|
|
134
137
|
* @param {CSN.Model} csn Clean input CSN
|
|
135
|
-
* @param {
|
|
138
|
+
* @param {ODataOptions} [options={}] Options
|
|
136
139
|
* @returns {oDataCSN} Return an oData-pre-processed CSN
|
|
137
140
|
*/
|
|
138
141
|
function odata(csn, options = {}) {
|
|
@@ -188,8 +191,11 @@ function forHdi(csn, options = {}) {
|
|
|
188
191
|
*/
|
|
189
192
|
function forHdbcds(csn, options = {}) {
|
|
190
193
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
191
|
-
internalOptions.
|
|
192
|
-
|
|
194
|
+
internalOptions.transformation = 'hdbcds';
|
|
195
|
+
|
|
196
|
+
const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
|
|
197
|
+
|
|
198
|
+
return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
/**
|
|
@@ -205,18 +211,7 @@ function sql(csn, options = {}) {
|
|
|
205
211
|
// we need the CSN for view sorting
|
|
206
212
|
internalOptions.toSql.csn = true;
|
|
207
213
|
|
|
208
|
-
|
|
209
|
-
if (isBetaEnabled(internalOptions, 'pretransformedCSN') && isPreTransformed(csn, 'sql')) {
|
|
210
|
-
internalOptions.noRecompile = true; // pre-transformed cannot be recompiled!
|
|
211
|
-
checkPreTransformedCsn(csn, internalOptions, relevantSqlOptions, warnAboutMismatchOdata, 'to.sql');
|
|
212
|
-
|
|
213
|
-
intermediateResult = backends.renderSqlWithCsn(csn, internalOptions);
|
|
214
|
-
// attach CSN for view sorting
|
|
215
|
-
intermediateResult.csn = cloneCsn(csn, internalOptions);
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
|
|
219
|
-
}
|
|
214
|
+
const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
|
|
220
215
|
|
|
221
216
|
const result = sortViews(intermediateResult);
|
|
222
217
|
|
|
@@ -445,16 +440,21 @@ hdi.migration = hdiMigration;
|
|
|
445
440
|
* @returns {HDBCDS} { <filename>:<content>, ...}
|
|
446
441
|
*/
|
|
447
442
|
function hdbcds(csn, options = {}) {
|
|
443
|
+
timetrace.start('to.hdbcds');
|
|
448
444
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
449
|
-
|
|
445
|
+
internalOptions.transformation = 'hdbcds';
|
|
450
446
|
|
|
451
|
-
|
|
447
|
+
const hanaCsn = forHdbcds(csn, internalOptions);
|
|
448
|
+
|
|
449
|
+
const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
|
|
450
|
+
timetrace.stop();
|
|
451
|
+
return result;
|
|
452
452
|
}
|
|
453
453
|
/**
|
|
454
454
|
* Generate a edm document for the given service
|
|
455
455
|
*
|
|
456
456
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
457
|
-
* @param {
|
|
457
|
+
* @param {ODataOptions} [options={}] Options
|
|
458
458
|
* @returns {edm} The JSON representation of the service
|
|
459
459
|
*/
|
|
460
460
|
function edm(csn, options = {}) {
|
|
@@ -484,7 +484,7 @@ edm.all = edmall;
|
|
|
484
484
|
* Generate edm documents for all services
|
|
485
485
|
*
|
|
486
486
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
487
|
-
* @param {
|
|
487
|
+
* @param {ODataOptions} [options={}] Options
|
|
488
488
|
* @returns {edms} { <service>:<JSON representation>, ...}
|
|
489
489
|
*/
|
|
490
490
|
function edmall(csn, options = {}) {
|
|
@@ -494,19 +494,17 @@ function edmall(csn, options = {}) {
|
|
|
494
494
|
if (internalOptions.version === 'v2')
|
|
495
495
|
error(null, null, 'OData JSON output is not available for OData V2');
|
|
496
496
|
|
|
497
|
-
let servicesAll;
|
|
498
|
-
|
|
499
497
|
const result = {};
|
|
498
|
+
let oDataCsn = csn;
|
|
500
499
|
|
|
501
|
-
if (isPreTransformed(csn, 'odata'))
|
|
500
|
+
if (isPreTransformed(csn, 'odata'))
|
|
502
501
|
checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const services = servicesAll.edmj;
|
|
502
|
+
|
|
503
|
+
else
|
|
504
|
+
oDataCsn = odataInternal(csn, internalOptions);
|
|
505
|
+
|
|
506
|
+
const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
|
|
507
|
+
const services = servicesJson.edmj;
|
|
510
508
|
for (const serviceName in services) {
|
|
511
509
|
const lEdm = services[serviceName];
|
|
512
510
|
// FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
|
|
@@ -518,7 +516,7 @@ function edmall(csn, options = {}) {
|
|
|
518
516
|
* Generate a edmx document for the given service
|
|
519
517
|
*
|
|
520
518
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
521
|
-
* @param {
|
|
519
|
+
* @param {ODataOptions} [options={}] Options
|
|
522
520
|
* @returns {edmx} The XML representation of the service
|
|
523
521
|
*/
|
|
524
522
|
function edmx(csn, options = {}) {
|
|
@@ -549,7 +547,7 @@ edmx.all = edmxall;
|
|
|
549
547
|
* Generate edmx documents for all services
|
|
550
548
|
*
|
|
551
549
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
552
|
-
* @param {
|
|
550
|
+
* @param {ODataOptions} [options={}] Options
|
|
553
551
|
* @returns {edmxs} { <service>:<XML representation>, ...}
|
|
554
552
|
*/
|
|
555
553
|
function edmxall(csn, options = {}) {
|
|
@@ -595,41 +593,6 @@ function flattenResultStructure(toProcess) {
|
|
|
595
593
|
return result;
|
|
596
594
|
}
|
|
597
595
|
|
|
598
|
-
/**
|
|
599
|
-
* Compute the .hdbcds files that would have been generated by the csn for an undeploy.json.
|
|
600
|
-
* This is needed for the handover between hdbcds and hdbtable - the existing hdbcds artifacts need to be undeployed.
|
|
601
|
-
*
|
|
602
|
-
* @param {any} csn A clean input CSN
|
|
603
|
-
* @param {hdbcdsOptions} [options={}] Options
|
|
604
|
-
* @returns {string[]} Array of .hdbcds filenames
|
|
605
|
-
*/
|
|
606
|
-
function undeploy(csn, options) {
|
|
607
|
-
const hdbcdsResult = hdbcds(csn, options);
|
|
608
|
-
|
|
609
|
-
return Object.keys(hdbcdsResult);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* This function simply renders the given CSN to SQL - no integrity checks, no transformations, no guarantees for correctness.
|
|
614
|
-
* Strictly for internal evaluation!
|
|
615
|
-
*
|
|
616
|
-
* @param {CSN.Model} csn A CSN - for things to work correctly, this is expected to be a DB transformed CSN. Plain CSN might work - or might not.
|
|
617
|
-
* @param {sqlOptions} [options={}] Options
|
|
618
|
-
* @returns {SQL[]} Array of SQL statements, tables first, views second - the resulting statements might not be a consistent, deployable state!
|
|
619
|
-
*/
|
|
620
|
-
function renderSQL(csn, options) {
|
|
621
|
-
const internalOptions = prepareOptions.to.sql(options);
|
|
622
|
-
if (!isBetaEnabled(internalOptions, 'renderSQL'))
|
|
623
|
-
throw new Error('renderSQL is only available with beta-flag "renderSQL".');
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
// Add flag so it thinks we ran through forHanaNew
|
|
627
|
-
internalOptions.forHana = true;
|
|
628
|
-
|
|
629
|
-
const sqlResult = toSqlDdl(csn, internalOptions);
|
|
630
|
-
return Object.values(flattenResultStructure(sqlResult));
|
|
631
|
-
}
|
|
632
|
-
|
|
633
596
|
module.exports = {
|
|
634
597
|
odata: publishCsnProcessor(odata, 'for.odata'),
|
|
635
598
|
cdl: publishCsnProcessor(cdl, 'to.cdl'),
|
|
@@ -642,45 +605,10 @@ module.exports = {
|
|
|
642
605
|
for_sql: publishCsnProcessor(forSql, 'for.sql'),
|
|
643
606
|
for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
|
|
644
607
|
for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
|
|
645
|
-
renderSQL,
|
|
646
|
-
undeploy,
|
|
647
608
|
/** */
|
|
648
609
|
};
|
|
649
610
|
|
|
650
611
|
|
|
651
|
-
/**
|
|
652
|
-
* Recompile the given CSN
|
|
653
|
-
*
|
|
654
|
-
* @param {object} csn Input CSN to recompile to XSN
|
|
655
|
-
* @param {object} options Options
|
|
656
|
-
* @returns {object} XSN
|
|
657
|
-
*
|
|
658
|
-
* TODO: move to lib/compiler/, consider new $recompile option, probaby issue
|
|
659
|
-
* message api-recompiled-csn there.
|
|
660
|
-
*/
|
|
661
|
-
function recompile(csn, options) {
|
|
662
|
-
// TODO: is it really a good idea to set options in the input parameter?
|
|
663
|
-
// (as long as we have the messages meddling of the option processor...)
|
|
664
|
-
// OK, I try a copy
|
|
665
|
-
options = { ...options };
|
|
666
|
-
// Explicitly set parseCdl to false because backends cannot handle
|
|
667
|
-
// the option and is only intended for CDL sources.
|
|
668
|
-
options.parseCdl = false; // TODO: delete this option
|
|
669
|
-
// Explicitly delete all toCsn options
|
|
670
|
-
delete options.toCsn;
|
|
671
|
-
/* eslint-disable global-require */
|
|
672
|
-
const { augment } = require('../json/from-csn');
|
|
673
|
-
const { compileSourcesX } = require('../compiler');
|
|
674
|
-
/* eslint-enable global-require */
|
|
675
|
-
const file = csn.$location && csn.$location.file &&
|
|
676
|
-
csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
|
|
677
|
-
const xsn = augment(csn, file, options); // in-place
|
|
678
|
-
const compiled = compileSourcesX( { [file]: xsn }, { ...options, $recompile: true } );
|
|
679
|
-
if (options.messages)
|
|
680
|
-
deduplicateMessages(options.messages);
|
|
681
|
-
return compiled;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
612
|
/**
|
|
685
613
|
* @param {any} processor CSN processor
|
|
686
614
|
* @param {string} _name Name of the processor
|
|
@@ -720,7 +648,7 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
720
648
|
const { info } = makeMessageFunction( csn, options, 'compile' );
|
|
721
649
|
info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
|
|
722
650
|
// next line to be replaced by CSN parser call which reads the CSN object
|
|
723
|
-
const xsn =
|
|
651
|
+
const xsn = recompileX(csn, options);
|
|
724
652
|
const recompiledCsn = compactModel(xsn);
|
|
725
653
|
return processor( recompiledCsn, options, ...args );
|
|
726
654
|
}
|
|
@@ -781,20 +709,6 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
781
709
|
* @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
|
|
782
710
|
*/
|
|
783
711
|
|
|
784
|
-
/**
|
|
785
|
-
* Options available for all oData-based functions
|
|
786
|
-
*
|
|
787
|
-
* @typedef {object} oDataOptions
|
|
788
|
-
* @property {object} [beta] Enable experimental features - not for productive use!
|
|
789
|
-
* @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
|
|
790
|
-
* @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
|
|
791
|
-
* @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
|
|
792
|
-
* @property {oDataVersion} [odataVersion='v4'] Odata version to use
|
|
793
|
-
* @property {oDataFormat} [odataFormat='flat'] Wether to generate oData as flat or as structured. Structured only with v4.
|
|
794
|
-
* @property {NamingMode} [sqlMapping='plain'] Naming mode to use
|
|
795
|
-
* @property {string} [service] If a single service is to be rendered
|
|
796
|
-
*/
|
|
797
|
-
|
|
798
712
|
/**
|
|
799
713
|
* Options available for to.hdi
|
|
800
714
|
*
|
|
@@ -825,9 +739,9 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
825
739
|
* @typedef {object} sqlOptions
|
|
826
740
|
* @property {NamingMode} [sqlMapping='plain'] Naming mode to use
|
|
827
741
|
* @property {SQLDialect} [sqlDialect='sqlite'] SQL dialect to use
|
|
828
|
-
* @property {object} [
|
|
829
|
-
* @property {string} [
|
|
830
|
-
* @property {string} [
|
|
742
|
+
* @property {object} [variableReplacements] Object containing values for magic variables like "$user"
|
|
743
|
+
* @property {string} [variableReplacements.$user.locale] Value for the "$user.locale" variable
|
|
744
|
+
* @property {string} [variableReplacements.$user.id] Value for the "$userid" variable
|
|
831
745
|
* @property {object} [beta] Enable experimental features - not for productive use!
|
|
832
746
|
* @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
|
|
833
747
|
* @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
|
package/lib/api/options.js
CHANGED
|
@@ -10,18 +10,22 @@ const publicOptionsNewAPI = [
|
|
|
10
10
|
// GENERAL
|
|
11
11
|
'beta',
|
|
12
12
|
'deprecated',
|
|
13
|
+
'addTextsLanguageAssoc',
|
|
13
14
|
'localizedLanguageFallback', // why can't I define the option type here?
|
|
14
15
|
'severities',
|
|
15
16
|
'messages',
|
|
16
17
|
'withLocations',
|
|
18
|
+
'defaultBinaryLength',
|
|
17
19
|
'defaultStringLength',
|
|
20
|
+
'csnFlavor',
|
|
18
21
|
// DB
|
|
19
22
|
'sqlDialect',
|
|
20
23
|
'sqlMapping',
|
|
21
24
|
'sqlChangeMode',
|
|
22
25
|
'allowCsnDowngrade',
|
|
23
26
|
'joinfk',
|
|
24
|
-
'magicVars',
|
|
27
|
+
'magicVars', // deprecated
|
|
28
|
+
'variableReplacements',
|
|
25
29
|
// ODATA
|
|
26
30
|
'odataVersion',
|
|
27
31
|
'odataFormat',
|
|
@@ -45,9 +49,10 @@ const privateOptions = [
|
|
|
45
49
|
'traceParserAmb',
|
|
46
50
|
'testMode',
|
|
47
51
|
'testSortCsn',
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
52
|
+
'integrityNotEnforced',
|
|
53
|
+
'integrityNotValidated',
|
|
54
|
+
'assertIntegrity',
|
|
55
|
+
'assertIntegrityType',
|
|
51
56
|
'noRecompile',
|
|
52
57
|
'internalMsg',
|
|
53
58
|
'disableHanaComments', // in case of issues with hana comment rendering
|
|
@@ -68,10 +73,11 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
|
|
|
68
73
|
* @param {FlatOptions} [hardRequire={}] Hard requirements to enforce
|
|
69
74
|
* @param {object} [customValidators] Custom validations to run instead of defaults
|
|
70
75
|
* @param {string[]} [combinationValidators] Option combinations to validate
|
|
76
|
+
* @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
|
|
71
77
|
* @returns {TranslatedOptions} General cds options
|
|
72
78
|
*/
|
|
73
79
|
function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
74
|
-
customValidators = {}, combinationValidators = []) {
|
|
80
|
+
customValidators = {}, combinationValidators = [], moduleName = '') {
|
|
75
81
|
const options = Object.assign({}, defaults);
|
|
76
82
|
const inputOptionNames = Object.keys(input);
|
|
77
83
|
for (const name of overallOptions) {
|
|
@@ -90,6 +96,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
90
96
|
// Validate the filtered input options
|
|
91
97
|
// only "new-style" options are here
|
|
92
98
|
validate(options,
|
|
99
|
+
moduleName,
|
|
93
100
|
// TODO: is there a better place to specify the type of option values?
|
|
94
101
|
Object.assign( {
|
|
95
102
|
localizedLanguageFallback: generateStringValidator([ 'none', 'coalesce' ]),
|
|
@@ -105,6 +112,10 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
105
112
|
mapToOldNames(optionName, optionValue);
|
|
106
113
|
}
|
|
107
114
|
|
|
115
|
+
// Convenience for $user -> $user.id replacement
|
|
116
|
+
if (options.variableReplacements && options.variableReplacements.$user && typeof options.variableReplacements.$user === 'string')
|
|
117
|
+
options.variableReplacements.$user = { id: options.variableReplacements.$user };
|
|
118
|
+
|
|
108
119
|
/**
|
|
109
120
|
* Map a new-style option to it's old format
|
|
110
121
|
*
|
|
@@ -126,6 +137,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
126
137
|
case 'sqlMapping':
|
|
127
138
|
options.names = optionValue;
|
|
128
139
|
break;
|
|
140
|
+
// No need to remap variableReplacements here - we use the new mechanism with that directly
|
|
129
141
|
case 'magicVars':
|
|
130
142
|
if (optionValue.user)
|
|
131
143
|
options.user = optionValue.user;
|
|
@@ -141,11 +153,11 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
141
153
|
|
|
142
154
|
module.exports = {
|
|
143
155
|
to: {
|
|
144
|
-
cdl: options => translateOptions(options),
|
|
156
|
+
cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
|
|
145
157
|
sql: (options) => {
|
|
146
158
|
const hardOptions = { src: 'sql' };
|
|
147
159
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
|
|
148
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ]);
|
|
160
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
|
|
149
161
|
|
|
150
162
|
const result = Object.assign({}, processed);
|
|
151
163
|
result.toSql = Object.assign({}, processed);
|
|
@@ -155,7 +167,7 @@ module.exports = {
|
|
|
155
167
|
hdi: (options) => {
|
|
156
168
|
const hardOptions = { src: 'hdi' };
|
|
157
169
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
158
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) });
|
|
170
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
|
|
159
171
|
|
|
160
172
|
const result = Object.assign({}, processed);
|
|
161
173
|
result.toSql = Object.assign({}, processed);
|
|
@@ -164,17 +176,17 @@ module.exports = {
|
|
|
164
176
|
},
|
|
165
177
|
hdbcds: (options) => {
|
|
166
178
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
167
|
-
const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) });
|
|
179
|
+
const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdbcds');
|
|
168
180
|
|
|
169
181
|
const result = Object.assign({}, processed);
|
|
170
|
-
result.
|
|
182
|
+
result.forHana = Object.assign({}, processed);
|
|
171
183
|
|
|
172
184
|
return result;
|
|
173
185
|
},
|
|
174
186
|
edm: (options) => {
|
|
175
187
|
const hardOptions = { json: true, combined: true };
|
|
176
188
|
const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
|
|
177
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ]);
|
|
189
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ], 'to.edm');
|
|
178
190
|
|
|
179
191
|
const result = Object.assign({}, processed);
|
|
180
192
|
result.toOdata = Object.assign({}, processed);
|
|
@@ -186,7 +198,7 @@ module.exports = {
|
|
|
186
198
|
const defaultOptions = {
|
|
187
199
|
odataVersion: 'v4', odataFormat: 'flat',
|
|
188
200
|
};
|
|
189
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ]);
|
|
201
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ], 'to.edmx');
|
|
190
202
|
|
|
191
203
|
const result = Object.assign({}, processed);
|
|
192
204
|
result.toOdata = Object.assign({}, processed);
|
|
@@ -198,7 +210,7 @@ module.exports = {
|
|
|
198
210
|
|
|
199
211
|
odata: (options) => {
|
|
200
212
|
const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
|
|
201
|
-
const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ]);
|
|
213
|
+
const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ], 'for.odata');
|
|
202
214
|
|
|
203
215
|
const result = Object.assign({}, processed);
|
|
204
216
|
result.toOdata = Object.assign({}, processed);
|
|
@@ -208,10 +220,10 @@ module.exports = {
|
|
|
208
220
|
},
|
|
209
221
|
hana: (options) => {
|
|
210
222
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
211
|
-
const processed = translateOptions(options, defaultOptions);
|
|
223
|
+
const processed = translateOptions(options, defaultOptions, undefined, undefined, undefined, 'for.hana');
|
|
212
224
|
|
|
213
225
|
const result = Object.assign({}, processed);
|
|
214
|
-
result.
|
|
226
|
+
result.forHana = Object.assign({}, processed);
|
|
215
227
|
|
|
216
228
|
|
|
217
229
|
return result;
|
package/lib/api/validate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { makeMessageFunction
|
|
3
|
+
const { makeMessageFunction } = require('../base/messages');
|
|
4
4
|
|
|
5
5
|
/* eslint-disable arrow-body-style */
|
|
6
6
|
const booleanValidator = {
|
|
@@ -70,6 +70,14 @@ const validators = {
|
|
|
70
70
|
return val === null ? val : `type ${ typeof val }`;
|
|
71
71
|
},
|
|
72
72
|
},
|
|
73
|
+
// TODO: Maybe do a deep validation of the whole object with leafs?
|
|
74
|
+
variableReplacements: {
|
|
75
|
+
validate: val => val !== null && typeof val === 'object' && !Array.isArray(val),
|
|
76
|
+
expected: () => 'type object',
|
|
77
|
+
found: (val) => {
|
|
78
|
+
return val === null ? val : `type ${ typeof val }`;
|
|
79
|
+
},
|
|
80
|
+
},
|
|
73
81
|
messages: {
|
|
74
82
|
validate: val => Array.isArray(val),
|
|
75
83
|
expected: () => 'type array',
|
|
@@ -89,14 +97,32 @@ const validators = {
|
|
|
89
97
|
expected: () => 'type array of string',
|
|
90
98
|
found: val => `type ${ typeof val }`,
|
|
91
99
|
},
|
|
100
|
+
defaultBinaryLength: {
|
|
101
|
+
validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
|
|
102
|
+
|
|
103
|
+
expected: () => 'Integer literal',
|
|
104
|
+
found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
|
|
105
|
+
},
|
|
92
106
|
defaultStringLength: {
|
|
93
|
-
validate: val => Number.isInteger(val),
|
|
107
|
+
validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
|
|
108
|
+
|
|
94
109
|
expected: () => 'Integer literal',
|
|
110
|
+
found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
|
|
111
|
+
},
|
|
112
|
+
csnFlavor: {
|
|
113
|
+
validate: val => typeof val === 'string',
|
|
114
|
+
expected: () => 'type string',
|
|
95
115
|
found: val => `type ${ typeof val }`,
|
|
96
116
|
},
|
|
97
117
|
dictionaryPrototype: {
|
|
98
118
|
validate: () => true,
|
|
99
119
|
},
|
|
120
|
+
assertIntegrity: {
|
|
121
|
+
validate: val => typeof val === 'string' && val === 'individual' || typeof val === 'boolean',
|
|
122
|
+
expected: () => 'a boolean or a string with value \'individual\'',
|
|
123
|
+
found: val => (typeof val === 'string' ? val : `type ${ typeof val }`),
|
|
124
|
+
},
|
|
125
|
+
assertIntegrityType: generateStringValidator([ 'DB', 'RT' ]),
|
|
100
126
|
};
|
|
101
127
|
|
|
102
128
|
const allCombinationValidators = {
|
|
@@ -122,15 +148,17 @@ const allCombinationValidators = {
|
|
|
122
148
|
* Use a custom validator or "default" custom validator, fallback to Boolean validator.
|
|
123
149
|
*
|
|
124
150
|
* @param {object} options Flat options object to validate
|
|
151
|
+
* @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
|
|
125
152
|
* @param {object} [customValidators] Map of custom validators to use
|
|
126
153
|
* @param {string[]} [combinationValidators] Validate option combinations
|
|
127
154
|
* @returns {void}
|
|
128
155
|
* @throws {CompilationError} Throws in case of invalid option usage
|
|
129
156
|
*/
|
|
130
|
-
function validate(options, customValidators = {}, combinationValidators = []) {
|
|
157
|
+
function validate(options, moduleName, customValidators = {}, combinationValidators = []) {
|
|
158
|
+
// TODO: issuing messages in this function looks very strange...
|
|
131
159
|
{
|
|
132
160
|
const messageCollector = { messages: [] };
|
|
133
|
-
const { error, throwWithError } = makeMessageFunction(null, messageCollector);
|
|
161
|
+
const { error, throwWithError } = makeMessageFunction(null, messageCollector, moduleName);
|
|
134
162
|
|
|
135
163
|
for (const optionName of Object.keys(options)) {
|
|
136
164
|
const optionValue = options[optionName];
|
|
@@ -142,7 +170,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
|
|
|
142
170
|
throwWithError();
|
|
143
171
|
}
|
|
144
172
|
|
|
145
|
-
const message = makeMessageFunction(null, options);
|
|
173
|
+
const message = makeMessageFunction(null, options, moduleName);
|
|
146
174
|
|
|
147
175
|
for (const combinationValidatorName of combinationValidators.concat([ 'beta-no-test' ])) {
|
|
148
176
|
const combinationValidator = allCombinationValidators[combinationValidatorName];
|
|
@@ -150,10 +178,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
|
|
|
150
178
|
message[combinationValidator.severity]('invalid-option-combination', null, {}, combinationValidator.getMessage(options));
|
|
151
179
|
}
|
|
152
180
|
|
|
153
|
-
|
|
154
|
-
// But be aware that it only throws with non-configurable errors and that this
|
|
155
|
-
// will lead to issues in test3. See #6037
|
|
156
|
-
handleMessages(undefined, options);
|
|
181
|
+
message.throwWithError();
|
|
157
182
|
}
|
|
158
183
|
/* eslint-enable jsdoc/no-undefined-types */
|
|
159
184
|
|
package/lib/backends.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
const { transformForHanaWithCsn } = require('./transform/forHanaNew');
|
|
7
7
|
const { compactModel, sortCsn } = require('./json/to-csn')
|
|
8
8
|
const { toCdsSourceCsn } = require('./render/toCdl');
|
|
9
|
-
const { toHdbcdsSource } = require('./render/toHdbcds');
|
|
10
9
|
const { toSqlDdl } = require('./render/toSql');
|
|
11
10
|
const { toRenameDdl } = require('./render/toRename');
|
|
12
11
|
const { manageConstraints, listReferentialIntegrityViolations } = require('./render/manageConstraints');
|
|
@@ -15,89 +14,10 @@ const { csn2edm, csn2edmAll } = require('./edm/csn2edm');
|
|
|
15
14
|
const { mergeOptions } = require('./model/csnUtils');
|
|
16
15
|
const { isBetaEnabled } = require('./base/model');
|
|
17
16
|
const { optionProcessor } = require('./optionProcessor')
|
|
18
|
-
const timetrace = require('./utils/timetrace');
|
|
17
|
+
const { timetrace } = require('./utils/timetrace');
|
|
19
18
|
const { makeMessageFunction } = require('./base/messages');
|
|
20
19
|
const { forEachDefinition } = require('./model/csnUtils');
|
|
21
20
|
|
|
22
|
-
/**
|
|
23
|
-
* Transform a CSN into HANA-compatible CDS source.
|
|
24
|
-
* The following options control what is actually generated (see help above):
|
|
25
|
-
* options : {
|
|
26
|
-
* toHana.names
|
|
27
|
-
* toHana.src
|
|
28
|
-
* toHana.csn
|
|
29
|
-
* }
|
|
30
|
-
* Options provided here are merged with (and take precedence over) options from 'model'.
|
|
31
|
-
* If 'toHana.names' is not provided, 'quoted' is used.
|
|
32
|
-
* If neither 'toHana.src' nor 'toHana.csn' are provided, the default is to generate only HANA CDS
|
|
33
|
-
* source files.
|
|
34
|
-
* If all provided options are part of 'toHana', the 'toHana' wrapper can be omitted.
|
|
35
|
-
* The result object contains the generation results as follows (as enabled in 'options'):
|
|
36
|
-
* result : {
|
|
37
|
-
* csn : the (compact) transformed CSN model
|
|
38
|
-
* hdbcds : a dictionary of top-level artifact names, containing for each name 'X':
|
|
39
|
-
* <X> : the HANA CDS source string of the artifact 'X'. Please note that the
|
|
40
|
-
* name of 'X' may contain characters that are not legal for filenames on
|
|
41
|
-
* all operating systems (e.g. ':', '\' or '/').
|
|
42
|
-
* X reflects the naming policy set by toHana.names
|
|
43
|
-
* }
|
|
44
|
-
* Throws a CompilationError on errors.
|
|
45
|
-
*
|
|
46
|
-
* @param {CSN.Model} csn
|
|
47
|
-
* @param {CSN.Options} [options]
|
|
48
|
-
*/
|
|
49
|
-
function toHanaWithCsn(csn, options) {
|
|
50
|
-
timetrace.start('toHanaWithCsn');
|
|
51
|
-
// In case of API usage the options are in the 'options' argument
|
|
52
|
-
// put the OData specific options under the 'options.toHana' wrapper
|
|
53
|
-
// and leave the rest under 'options'
|
|
54
|
-
if (options && !options.toHana) {
|
|
55
|
-
_wrapRelevantOptionsForCmd(options, 'toHana');
|
|
56
|
-
}
|
|
57
|
-
// Provide defaults and merge options with those from model
|
|
58
|
-
options = mergeOptions({ toHana : getDefaultBackendOptions().toHana }, options);
|
|
59
|
-
|
|
60
|
-
// Provide something to generate if nothing else was given (conditional default)
|
|
61
|
-
if (!options.toHana.src && !options.toHana.csn) {
|
|
62
|
-
options.toHana.src = true;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const { warning } = makeMessageFunction(csn, options, 'to.hdbcds');
|
|
66
|
-
|
|
67
|
-
// Verify options
|
|
68
|
-
optionProcessor.verifyOptions(options, 'toHana', true).forEach(complaint => warning(null, null, `${complaint}`));
|
|
69
|
-
|
|
70
|
-
// Special case: For naming variant 'hdbcds' in combination with 'toHana' (and only there!), 'forHana'
|
|
71
|
-
// must leave namespaces, structs and associations alone.
|
|
72
|
-
if (options.toHana.names === 'hdbcds') {
|
|
73
|
-
options = mergeOptions(options, { forHana : { keepNamespaces: true, keepStructsAssocs: true } });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
options = mergeOptions(options, { forHana: { dialect: 'hana' } }, { forHana : options.toHana });
|
|
77
|
-
|
|
78
|
-
// Prepare model for HANA (transferring the options to forHana, and setting 'dialect' to 'hana', because 'toHana' is only used for that)
|
|
79
|
-
let forHanaCsn = transformForHanaWithCsn(csn, options, 'to.hdbcds');
|
|
80
|
-
|
|
81
|
-
// Assemble result
|
|
82
|
-
let result = {};
|
|
83
|
-
|
|
84
|
-
if (options.toHana.src) {
|
|
85
|
-
if(options.testMode){
|
|
86
|
-
const sorted = sortCsn(forHanaCsn, options);
|
|
87
|
-
result = toHdbcdsSource(sorted, options);
|
|
88
|
-
} else {
|
|
89
|
-
result = toHdbcdsSource(forHanaCsn, options);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (options.toHana.csn) {
|
|
94
|
-
result.csn = options.testMode ? sortCsn(forHanaCsn, options) : forHanaCsn;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
timetrace.stop();
|
|
98
|
-
return result;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
21
|
/**
|
|
102
22
|
* Generate ODATA for `csn` using `options`.
|
|
103
23
|
* The twin of the toOdata function but using CSN
|
|
@@ -172,9 +92,9 @@ function toOdataWithCsn(csn, options) {
|
|
|
172
92
|
// Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
173
93
|
// using 'options'
|
|
174
94
|
function preparedCsnToEdmx(csn, service, options) {
|
|
175
|
-
|
|
95
|
+
const e = csn2edm(csn, service, options)
|
|
176
96
|
return {
|
|
177
|
-
edmx
|
|
97
|
+
edmx: (e ? e.toXML('all') : undefined)
|
|
178
98
|
};
|
|
179
99
|
}
|
|
180
100
|
|
|
@@ -195,9 +115,9 @@ function preparedCsnToEdmxAll(csn, options) {
|
|
|
195
115
|
function preparedCsnToEdm(csn, service, options) {
|
|
196
116
|
// Merge options; override OData version as edm json is always v4
|
|
197
117
|
options = mergeOptions(options, { toOdata : { version : 'v4' }});
|
|
198
|
-
const
|
|
118
|
+
const e = csn2edm(csn, service, options);
|
|
199
119
|
return {
|
|
200
|
-
edmj
|
|
120
|
+
edmj: (e ? e.toJSON() : undefined)
|
|
201
121
|
};
|
|
202
122
|
}
|
|
203
123
|
|
|
@@ -485,9 +405,6 @@ function toRenameWithCsn(csn, options) {
|
|
|
485
405
|
|
|
486
406
|
function alterConstraintsWithCsn(csn, options) {
|
|
487
407
|
const { error } = makeMessageFunction(csn, options, 'manageConstraints');
|
|
488
|
-
// Requires beta mode
|
|
489
|
-
if (!isBetaEnabled(options, 'foreignKeyConstraints'))
|
|
490
|
-
error(null, null, 'ALTER TABLE statements for adding/modifying referential constraints are only available in beta mode');
|
|
491
408
|
|
|
492
409
|
const {
|
|
493
410
|
drop, alter, names, src, violations
|
|
@@ -500,6 +417,10 @@ function alterConstraintsWithCsn(csn, options) {
|
|
|
500
417
|
dialect: 'hana',
|
|
501
418
|
names: names || 'plain'
|
|
502
419
|
}
|
|
420
|
+
|
|
421
|
+
// Of course we want the database constraints
|
|
422
|
+
options.assertIntegrityType = 'DB';
|
|
423
|
+
|
|
503
424
|
const transformedOptions = transformSQLOptions(csn, options);
|
|
504
425
|
const mergedOptions = mergeOptions(transformedOptions.options, { forHana : transformedOptions.forHanaOptions });
|
|
505
426
|
const forSqlCsn = transformForHanaWithCsn(csn, mergedOptions, 'to.sql');
|
|
@@ -603,7 +524,6 @@ function _wrapRelevantOptionsForCmd(options, command) {
|
|
|
603
524
|
}
|
|
604
525
|
|
|
605
526
|
module.exports = {
|
|
606
|
-
toHanaWithCsn,
|
|
607
527
|
toOdataWithCsn,
|
|
608
528
|
preparedCsnToEdmx,
|
|
609
529
|
preparedCsnToEdmxAll,
|