@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
|
@@ -41,9 +41,7 @@ function forEachDefinition(csn, cb) {
|
|
|
41
41
|
* in HANA CDS style, used by 'toHana', toSql' and 'toRename'.
|
|
42
42
|
* The behavior is controlled by the following options:
|
|
43
43
|
* options = {
|
|
44
|
-
*
|
|
45
|
-
* forHana.alwaysResolveDerivedTypes // Always resolve derived type chains (by default, this is only
|
|
46
|
-
* // done for 'quoted' names). FIXME: Should always be done in general.
|
|
44
|
+
* sqlMapping // See the behavior of 'sqlMapping' in toHana, toSql and toRename
|
|
47
45
|
* }
|
|
48
46
|
* The result model will always have 'options.forHana' set, to indicate that these transformations have happened.
|
|
49
47
|
* The following transformations are made:
|
|
@@ -69,9 +67,9 @@ function forEachDefinition(csn, cb) {
|
|
|
69
67
|
* - (110) Actions and functions (bound or unbound) are ignored.
|
|
70
68
|
* - (120) (a) Services become contexts.
|
|
71
69
|
* - (130) (not for to.hdbcds with hdbcds names): Elements having structured types are flattened into
|
|
72
|
-
* multiple elements (using '_' or '.' as name separator, depending on '
|
|
70
|
+
* multiple elements (using '_' or '.' as name separator, depending on 'sqlMapping').
|
|
73
71
|
* - (140) (not for to.hdbcds with hdbcds names): Managed associations get explicit ON-conditions, with
|
|
74
|
-
* generated foreign key elements (also using '_' or '.' as name separator, depending on '
|
|
72
|
+
* generated foreign key elements (also using '_' or '.' as name separator, depending on 'sqlMapping').
|
|
75
73
|
* - (150) (a) Elements from inherited (included) entities are copied into the receiving entity
|
|
76
74
|
* (b) The 'include' property is removed from entities.
|
|
77
75
|
* - (160) Projections become views, with MIXINs for association elements (adding $projection where
|
|
@@ -94,7 +92,7 @@ function forEachDefinition(csn, cb) {
|
|
|
94
92
|
* (d) Managed association entries in ORDER BY
|
|
95
93
|
* - (240) All artifacts (a), elements, foreign keys, parameters (b) that have a DB representation are annotated
|
|
96
94
|
* with their database name (as '@cds.persistence.name') according to the naming convention chosen
|
|
97
|
-
* in 'options.
|
|
95
|
+
* in 'options.sqlMapping'.
|
|
98
96
|
* - (250) Remove name space definitions again (only in forHanaNew). Maybe we can omit inserting namespace definitions
|
|
99
97
|
* completely (TODO)
|
|
100
98
|
*
|
|
@@ -108,17 +106,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
108
106
|
/** @type {CSN.Model} */
|
|
109
107
|
let csn = cloneCsnNonDict(inputModel, options);
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
109
|
checkCSNVersion(csn, options);
|
|
114
110
|
|
|
115
|
-
const pathDelimiter = (options.
|
|
111
|
+
const pathDelimiter = (options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
116
112
|
|
|
117
113
|
let message, error, warning, info; // message functions
|
|
118
114
|
/** @type {() => void} */
|
|
119
115
|
let throwWithAnyError;
|
|
120
116
|
let artifactRef, inspectRef, effectiveType, get$combined,
|
|
121
|
-
getFinalBaseType, // csnUtils (csnRefs)
|
|
122
117
|
addDefaultTypeFacets, expandStructsInExpression; // transformUtils
|
|
123
118
|
|
|
124
119
|
bindCsnReference();
|
|
@@ -130,14 +125,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
130
125
|
bindCsnReference();
|
|
131
126
|
}
|
|
132
127
|
|
|
133
|
-
const dialect = options.
|
|
128
|
+
const dialect = options.sqlDialect;
|
|
134
129
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
135
130
|
if (!doA2J)
|
|
136
131
|
forEachDefinition(csn, handleMixinOnConditions);
|
|
137
132
|
|
|
138
133
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
139
134
|
const cleanup = validate.forHana(csn, {
|
|
140
|
-
message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options,
|
|
135
|
+
message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, isAspect
|
|
141
136
|
});
|
|
142
137
|
|
|
143
138
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
@@ -222,6 +217,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
222
217
|
flattenStructuredElement,
|
|
223
218
|
flattenStructStepsInRef,
|
|
224
219
|
isAssociationOperand, isDollarSelfOrProjectionOperand,
|
|
220
|
+
csnUtils,
|
|
225
221
|
} = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
226
222
|
|
|
227
223
|
const {
|
|
@@ -229,7 +225,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
229
225
|
isAssocOrComposition,
|
|
230
226
|
addStringAnnotationTo,
|
|
231
227
|
cloneWithTransformations,
|
|
232
|
-
} =
|
|
228
|
+
} = csnUtils;
|
|
233
229
|
|
|
234
230
|
// (000) Rename primitive types, make UUID a String
|
|
235
231
|
transformCsn(csn, {
|
|
@@ -269,10 +265,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
269
265
|
// To be done after handleAssociations, since then the foreign keys of the managed assocs
|
|
270
266
|
// are part of the elements
|
|
271
267
|
if (doA2J)
|
|
272
|
-
forEachDefinition(csn, associations.
|
|
268
|
+
forEachDefinition(csn, associations.getFKAccessFinalizer(csn, pathDelimiter));
|
|
273
269
|
|
|
274
270
|
// Create convenience views for localized entities/views.
|
|
275
|
-
// To be done after
|
|
271
|
+
// To be done after getFKAccessFinalizer because associations are
|
|
276
272
|
// handled and before handleDBChecks which removes the localized attribute.
|
|
277
273
|
// Association elements of localized convenience views do not have hidden properties
|
|
278
274
|
// like $managed set, so we cannot do this earlier on.
|
|
@@ -396,6 +392,15 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
396
392
|
setProp(SET, 'elements', query.SELECT.elements);
|
|
397
393
|
}
|
|
398
394
|
}
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if(options.sqlDialect === 'postgres') {
|
|
400
|
+
killers.length = (parent) => {
|
|
401
|
+
if (parent.type === 'cds.Binary' || parent.type === 'cds.hana.BINARY') {
|
|
402
|
+
delete parent.length;
|
|
403
|
+
}
|
|
399
404
|
}
|
|
400
405
|
}
|
|
401
406
|
|
|
@@ -409,8 +414,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
409
414
|
|
|
410
415
|
function bindCsnReference(){
|
|
411
416
|
({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
|
|
412
|
-
({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
|
|
413
417
|
({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
418
|
+
// TODO: Can we use csnUtils of the call above (transformUtils.getTransformers)?
|
|
419
|
+
({ artifactRef, inspectRef, effectiveType, get$combined } = getUtils(csn));
|
|
414
420
|
}
|
|
415
421
|
|
|
416
422
|
function bindCsnReferenceOnly(){
|
|
@@ -509,7 +515,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
509
515
|
function recursivelyApplyCommon(artifact, artifactName) {
|
|
510
516
|
if (!artifact._ignore) {
|
|
511
517
|
if (artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
512
|
-
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.
|
|
518
|
+
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
513
519
|
|
|
514
520
|
forEachMemberRecursively(artifact, (member, memberName, property, path) => {
|
|
515
521
|
transformCommon(member, memberName, path);
|
|
@@ -517,7 +523,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
517
523
|
// Virtual elements in entities and types are not annotated, as they have no DB representation.
|
|
518
524
|
// In views they are, as we generate a null expression for them (null as <colname>)
|
|
519
525
|
if ((!member.virtual || artifact.query))
|
|
520
|
-
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.
|
|
526
|
+
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
|
|
521
527
|
}, [ 'definitions', artifactName ]);
|
|
522
528
|
}
|
|
523
529
|
}
|
|
@@ -543,7 +549,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
543
549
|
function transformSelfInBacklinks(artifact, artifactName, dummy, path) {
|
|
544
550
|
// Fixme: For toHana mixins must be transformed, for toSql -d hana
|
|
545
551
|
// mixin elements must be transformed, why can't toSql also use mixins?
|
|
546
|
-
if(artifact.kind === 'entity' || artifact.query || (options.
|
|
552
|
+
if(artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
|
|
547
553
|
doit(artifact.elements, path.concat([ 'elements' ]));
|
|
548
554
|
if (artifact.query && artifact.query.SELECT && artifact.query.SELECT.mixin)
|
|
549
555
|
doit(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
|
|
@@ -574,7 +580,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
574
580
|
// For HANA: Report an error on
|
|
575
581
|
// - view with parameters that has an element of type association/composition
|
|
576
582
|
// - association that points to entity with parameters
|
|
577
|
-
if (options.
|
|
583
|
+
if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
578
584
|
if (artifact.params) {
|
|
579
585
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
580
586
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
@@ -617,7 +623,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
617
623
|
sql: 'Table-like entities with parameters are not supported for conversion to SQL',
|
|
618
624
|
});
|
|
619
625
|
}
|
|
620
|
-
else if (options.
|
|
626
|
+
else if (options.sqlDialect === 'sqlite') { // view with params
|
|
621
627
|
// Allow with plain
|
|
622
628
|
error(null, [ 'definitions', artifactName ], `SQLite does not support entities with parameters`);
|
|
623
629
|
}
|
|
@@ -626,8 +632,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
626
632
|
if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
|
|
627
633
|
warning(null, [ 'definitions', artifactName, 'params', pname ], `Expecting regular SQL-Identifier`);
|
|
628
634
|
}
|
|
629
|
-
else if (options.
|
|
630
|
-
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.
|
|
635
|
+
else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
|
|
636
|
+
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
|
|
631
637
|
'Expecting parameter to be uppercase in naming mode $(NAME)');
|
|
632
638
|
}
|
|
633
639
|
}
|
|
@@ -1048,7 +1054,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1048
1054
|
* @returns {boolean}
|
|
1049
1055
|
*/
|
|
1050
1056
|
function isMaxParameterLengthRestricted(type) {
|
|
1051
|
-
return !(options.toSql && type === 'cds.String' && (options.
|
|
1057
|
+
return !(options.toSql && type === 'cds.String' && (options.sqlDialect === 'sqlite' || options.sqlDialect === 'plain'));
|
|
1052
1058
|
}
|
|
1053
1059
|
|
|
1054
1060
|
/**
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
4
|
const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
5
5
|
const transformUtils = require('./transformUtilsNew');
|
|
6
|
-
const {
|
|
7
|
-
cloneCsnNonDict,
|
|
6
|
+
const { cloneCsnNonDict,
|
|
8
7
|
forEachDefinition,
|
|
9
8
|
forEachMemberRecursively,
|
|
10
9
|
applyTransformationsOnNonDictionary,
|
|
@@ -86,10 +85,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
86
85
|
extractValidFromToKeyElement,
|
|
87
86
|
checkAssignment, checkMultipleAssignments,
|
|
88
87
|
recurseElements, setAnnotation, renameAnnotation,
|
|
89
|
-
expandStructsInExpression
|
|
88
|
+
expandStructsInExpression,
|
|
89
|
+
csnUtils,
|
|
90
90
|
} = transformers;
|
|
91
91
|
|
|
92
|
-
const csnUtils = getUtils(csn);
|
|
93
92
|
const {
|
|
94
93
|
getCsnDef,
|
|
95
94
|
getServiceName,
|
|
@@ -98,11 +97,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
98
97
|
inspectRef,
|
|
99
98
|
artifactRef,
|
|
100
99
|
effectiveType,
|
|
101
|
-
getFinalBaseType,
|
|
102
100
|
} = csnUtils;
|
|
103
101
|
|
|
104
102
|
// are we working with structured OData or not
|
|
105
|
-
const structuredOData = options.
|
|
103
|
+
const structuredOData = options.odataFormat === 'structured' && options.odataVersion === 'v4';
|
|
106
104
|
|
|
107
105
|
// collect all declared non-abstract services from the model
|
|
108
106
|
// use the array when there is a need to identify if an artifact is in a service or not
|
|
@@ -115,17 +113,17 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
115
113
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
116
114
|
enrichUniversalCsn(csn, options);
|
|
117
115
|
|
|
118
|
-
const keepLocalizedViews = isDeprecatedEnabled(options, '
|
|
116
|
+
const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
|
|
119
117
|
|
|
120
118
|
function acceptLocalizedView(_name, parent) {
|
|
121
119
|
csn.definitions[parent].$localized = true;
|
|
122
120
|
return keepLocalizedViews && !isExternalServiceMember(undefined, parent);
|
|
123
121
|
}
|
|
124
122
|
|
|
125
|
-
addLocalizationViews(csn, options, acceptLocalizedView);
|
|
123
|
+
addLocalizationViews(csn, options, { acceptLocalizedView, ignoreUnknownExtensions: true });
|
|
126
124
|
|
|
127
125
|
const cleanup = validate.forOdata(csn, {
|
|
128
|
-
message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services,
|
|
126
|
+
message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
|
|
129
127
|
});
|
|
130
128
|
|
|
131
129
|
|
|
@@ -180,7 +178,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
180
178
|
// To be done after handleManagedAssociationsAndCreateForeignKeys,
|
|
181
179
|
// since then the foreign keys of the managed assocs are part of the elements
|
|
182
180
|
if(!structuredOData)
|
|
183
|
-
forEachDefinition(csn, associations.
|
|
181
|
+
forEachDefinition(csn, associations.getFKAccessFinalizer(csn, '_'));
|
|
184
182
|
|
|
185
183
|
// structure flattener reports errors, further processing is not safe -> throw exception in case of errors
|
|
186
184
|
throwWithAnyError();
|
|
@@ -214,16 +212,16 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
214
212
|
|
|
215
213
|
// Annotate artifacts with their DB names if requested.
|
|
216
214
|
// Skip artifacts that have no DB equivalent anyway
|
|
217
|
-
if (options.
|
|
218
|
-
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.
|
|
215
|
+
if (options.sqlMapping && !(def.kind in skipPersNameKinds))
|
|
216
|
+
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
|
|
219
217
|
|
|
220
218
|
forEachMemberRecursively(def, (member, memberName, propertyName) => {
|
|
221
219
|
// Annotate elements, foreign keys, parameters, etc. with their DB names if requested
|
|
222
220
|
// Only these are actually required and don't annotate virtual elements in entities or types
|
|
223
221
|
// as they have no DB representation (although in views)
|
|
224
|
-
if (options.
|
|
222
|
+
if (options.sqlMapping && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
|
|
225
223
|
// If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
|
|
226
|
-
member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.
|
|
224
|
+
member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
|
|
227
225
|
}
|
|
228
226
|
|
|
229
227
|
// Mark fields with @odata.on.insert/update as @Core.Computed
|
|
@@ -326,7 +324,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
326
324
|
// but '@Core.Immutable' for everything else.
|
|
327
325
|
if (!(node['@readonly'] && node['@insertonly'])) {
|
|
328
326
|
if (name === '@readonly' && node[name] !== null) {
|
|
329
|
-
if (node.kind === 'entity') {
|
|
327
|
+
if (node.kind === 'entity' || node.kind === 'aspect') {
|
|
330
328
|
setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
|
|
331
329
|
setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
|
|
332
330
|
setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
|
|
@@ -336,7 +334,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
336
334
|
}
|
|
337
335
|
// @insertonly is effective on entities/queries only
|
|
338
336
|
else if (name === '@insertonly' && node[name] !== null) {
|
|
339
|
-
if (node.kind === 'entity') {
|
|
337
|
+
if (node.kind === 'entity' || node.kind === 'aspect') {
|
|
340
338
|
setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
|
|
341
339
|
setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
|
|
342
340
|
setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
|
-
const { setProp } = require('../base/model');
|
|
4
|
+
const { setProp, isDeprecatedEnabled} = require('../base/model');
|
|
5
5
|
const { hasErrors } = require('../base/messages');
|
|
6
|
-
const {
|
|
6
|
+
const { forEachKey } = require('../utils/objectUtils');
|
|
7
7
|
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
8
8
|
const {
|
|
9
|
+
cloneCsnDictionary,
|
|
9
10
|
cloneCsnNonDict,
|
|
11
|
+
applyAnnotationsFromExtensions,
|
|
10
12
|
forEachDefinition,
|
|
11
13
|
forEachGeneric,
|
|
12
14
|
forAllQueries,
|
|
@@ -67,9 +69,9 @@ const _targetFor = Symbol('_targetFor');
|
|
|
67
69
|
* @param {CSN.Options} options
|
|
68
70
|
* @param {boolean} useJoins If true, rewrite the "localized" association to a
|
|
69
71
|
* join in direct convenience views.
|
|
70
|
-
* @param {
|
|
72
|
+
* @param {object} config
|
|
71
73
|
*/
|
|
72
|
-
function _addLocalizationViews(csn, options, useJoins,
|
|
74
|
+
function _addLocalizationViews(csn, options, useJoins, config) {
|
|
73
75
|
// Don't try to create convenience views with errors.
|
|
74
76
|
if (hasErrors(options.messages))
|
|
75
77
|
return csn;
|
|
@@ -78,6 +80,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
78
80
|
if (hasExistingLocalizationViews(csn, options, messageFunctions))
|
|
79
81
|
return csn;
|
|
80
82
|
|
|
83
|
+
const { acceptLocalizedView, ignoreUnknownExtensions } = config;
|
|
81
84
|
const noCoalesce = (options.localizedLanguageFallback === 'none' ||
|
|
82
85
|
options.localizedWithoutCoalesce);
|
|
83
86
|
|
|
@@ -87,9 +90,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
87
90
|
forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
|
|
88
91
|
|
|
89
92
|
// In case that the user tried to annotate `localized.*` artifacts, apply them.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
filter: (name) => name.startsWith('localized.')
|
|
93
|
+
applyAnnotationsFromExtensions(csn, {
|
|
94
|
+
override: true,
|
|
95
|
+
filter: (name) => name.startsWith('localized.'),
|
|
96
|
+
notFound(name, index) {
|
|
97
|
+
if (!ignoreUnknownExtensions)
|
|
98
|
+
messageFunctions.message('anno-undefined-art', [ 'extensions', index ], { name })
|
|
99
|
+
},
|
|
93
100
|
});
|
|
94
101
|
|
|
95
102
|
sortCsnDefinitionsForTests(csn, options);
|
|
@@ -147,7 +154,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
147
154
|
else
|
|
148
155
|
view = createLocalizedViewForEntity(art, artName, textElements);
|
|
149
156
|
|
|
150
|
-
copyPersistenceAnnotations(view, art);
|
|
157
|
+
copyPersistenceAnnotations(view, art, options);
|
|
151
158
|
csn.definitions[viewName] = view;
|
|
152
159
|
}
|
|
153
160
|
|
|
@@ -605,10 +612,10 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
605
612
|
*
|
|
606
613
|
* @param {CSN.Model} csn
|
|
607
614
|
* @param {CSN.Options} options
|
|
608
|
-
* @param [
|
|
615
|
+
* @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
|
|
609
616
|
*/
|
|
610
|
-
function addLocalizationViews(csn, options,
|
|
611
|
-
return _addLocalizationViews(csn, options, false,
|
|
617
|
+
function addLocalizationViews(csn, options, config = {}) {
|
|
618
|
+
return _addLocalizationViews(csn, options, false, config);
|
|
612
619
|
}
|
|
613
620
|
|
|
614
621
|
/**
|
|
@@ -618,10 +625,10 @@ function addLocalizationViews(csn, options, acceptLocalizedView = null) {
|
|
|
618
625
|
*
|
|
619
626
|
* @param {CSN.Model} csn
|
|
620
627
|
* @param {CSN.Options} options
|
|
621
|
-
* @param [
|
|
628
|
+
* @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
|
|
622
629
|
*/
|
|
623
|
-
function addLocalizationViewsWithJoins(csn, options,
|
|
624
|
-
return _addLocalizationViews(csn, options, true,
|
|
630
|
+
function addLocalizationViewsWithJoins(csn, options, config = {}) {
|
|
631
|
+
return _addLocalizationViews(csn, options, true, config);
|
|
625
632
|
}
|
|
626
633
|
|
|
627
634
|
/**
|
|
@@ -667,21 +674,24 @@ function copyLocation(target, source) {
|
|
|
667
674
|
}
|
|
668
675
|
|
|
669
676
|
/**
|
|
670
|
-
* Copy
|
|
671
|
-
* the target. Ignores existing annotations on the
|
|
677
|
+
* Copy @cds.persistence.exists/skip annotations from the source to
|
|
678
|
+
* the target. Ignores existing annotations on the _target_.
|
|
672
679
|
*
|
|
673
680
|
* @param {CSN.Artifact} target
|
|
674
681
|
* @param {CSN.Artifact} source
|
|
682
|
+
* @param {CSN.Options} options
|
|
675
683
|
*/
|
|
676
|
-
function copyPersistenceAnnotations(target, source) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
684
|
+
function copyPersistenceAnnotations(target, source, options) {
|
|
685
|
+
const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
|
|
686
|
+
forEachKey(source, anno => {
|
|
687
|
+
// Note:
|
|
688
|
+
// Because `.exists` is copied to the convenience view, it could
|
|
689
|
+
// lead to some localization views referencing non-existing ones.
|
|
690
|
+
// But that is the contract: User says that it already exists!
|
|
691
|
+
// In v2, `.exists` was never copied.
|
|
692
|
+
if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
|
|
693
|
+
target[anno] = source[anno];
|
|
694
|
+
});
|
|
685
695
|
}
|
|
686
696
|
|
|
687
697
|
/**
|
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
const { isArtifactInSomeService, isArtifactInService } = require('./utils');
|
|
8
8
|
|
|
9
9
|
function expandToFinalBaseType(csn, transformers, csnUtils, services, options, isExternalServiceMember) {
|
|
10
|
-
const isV4 = options.
|
|
10
|
+
const isV4 = options.odataVersion === 'v4';
|
|
11
11
|
forEachDefinition(csn, (def, defName) => {
|
|
12
12
|
// Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
|
|
13
13
|
forEachMemberRecursively(def, (member) => {
|
|
@@ -87,9 +87,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
87
87
|
if (node.kind === 'event') return;
|
|
88
88
|
|
|
89
89
|
// elements have precedence over type
|
|
90
|
-
if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
|
|
90
|
+
if (node.type && (!isBuiltinType(node.type) && isExpandable(node, defName) || node.kind === 'type')) {
|
|
91
91
|
// 1. Get the final type of the node (resolve derived type chain)
|
|
92
|
-
const finalType = csnUtils.
|
|
92
|
+
const finalType = csnUtils.getFinalBaseTypeWithProps(node.type);
|
|
93
93
|
if (finalType) {
|
|
94
94
|
// The type replacement depends on whether 'node' is a definition or a member[element].
|
|
95
95
|
if (node.kind) {
|
|
@@ -101,7 +101,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
101
101
|
// type A: B; -> {...}
|
|
102
102
|
// type B: C; -> { ... }
|
|
103
103
|
// type C { .... };
|
|
104
|
-
if (isBuiltinType(finalType)) {
|
|
104
|
+
if (isBuiltinType(finalType.type)) {
|
|
105
105
|
// use transformUrilsNew::toFinalBaseType for the moment,
|
|
106
106
|
// as it is collects along the chain of types
|
|
107
107
|
// attributes that need to be propagated
|
|
@@ -129,8 +129,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
129
129
|
// type A: B; -> {...}
|
|
130
130
|
// type B: C; -> { ... }
|
|
131
131
|
// type C { .... };
|
|
132
|
-
if (isBuiltinType(finalType)) {
|
|
133
|
-
// use
|
|
132
|
+
if (isBuiltinType(finalType.type)) {
|
|
133
|
+
// use transformUtilsNew::toFinalBaseType for the moment,
|
|
134
134
|
// as it is collects along the chain of types
|
|
135
135
|
// attributes that need to be propagated
|
|
136
136
|
// enum, length, scale, etc.
|
|
@@ -148,8 +148,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
148
148
|
// example in actions: 'action act() return Primitive; type Primitive: array of String;'
|
|
149
149
|
const currService = csnUtils.getServiceName(defName);
|
|
150
150
|
const finalType = csnUtils.getFinalTypeDef(node.type);
|
|
151
|
-
if (finalType.items &&
|
|
152
|
-
(isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.
|
|
151
|
+
if (finalType.items &&
|
|
152
|
+
(isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
|
|
153
153
|
{
|
|
154
154
|
if (!isArtifactInService(node.type, currService) || !isV4) {
|
|
155
155
|
node.items = finalType.items;
|
|
@@ -186,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
186
186
|
function isUserDefinedBuiltinFromTheCurrService(node, defName) {
|
|
187
187
|
// in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
|
|
188
188
|
// a builtin from the service - do not expand to the final base type
|
|
189
|
-
let finalBaseType = csnUtils.
|
|
190
|
-
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.
|
|
189
|
+
let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
|
|
190
|
+
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
|
|
191
191
|
const currService = csnUtils.getServiceName(defName);
|
|
192
192
|
return node.type && !node.type.ref
|
|
193
193
|
&& isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
|
|
@@ -10,11 +10,12 @@ const { setProp } = require('../../base/model');
|
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
11
|
const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
|
|
12
12
|
const { copyAnnotations } = require('../../model/csnUtils');
|
|
13
|
+
const { isBetaEnabled } = require('../../base/model.js');
|
|
13
14
|
|
|
14
15
|
function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
|
|
15
16
|
const { error } = message;
|
|
16
17
|
// are we working with OData proxies or cross-service refs
|
|
17
|
-
const isMultiSchema = options.
|
|
18
|
+
const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.odataXServiceRefs);
|
|
18
19
|
// collect in this variable all the newly exposed types
|
|
19
20
|
const schemas = Object.create(null);
|
|
20
21
|
const exposedTypes = Object.create(null);
|
|
@@ -92,11 +93,16 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
92
93
|
: getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
|
|
93
94
|
: `${serviceName}.${newTypeName}`;
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
isKey
|
|
99
|
-
|
|
96
|
+
if (!isAnonymous) {
|
|
97
|
+
// as soon as we leave of the anonymous world,
|
|
98
|
+
// we're no longer in a key def => don't set notNull:true on named types
|
|
99
|
+
if(isKey)
|
|
100
|
+
isKey = false;
|
|
101
|
+
// in case this was a named type and if the openess does not match the type definition
|
|
102
|
+
// expose the type as a new one not changing the original definition.
|
|
103
|
+
if((!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
|
|
104
|
+
fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
|
|
105
|
+
}
|
|
100
106
|
// check if that type is already defined
|
|
101
107
|
let newType = csn.definitions[fullQualifiedNewTypeName];
|
|
102
108
|
if (newType) {
|
|
@@ -111,6 +117,9 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
111
117
|
* Treat items.elements as ordinary elements for now.
|
|
112
118
|
*/
|
|
113
119
|
newType = createNewStructType(elements);
|
|
120
|
+
// if using node enforces open/closed, set it on type
|
|
121
|
+
if(node['@open'] !== undefined)
|
|
122
|
+
newType['@open'] = node['@open']
|
|
114
123
|
if (node.$location)
|
|
115
124
|
setProp(newType, '$location', node.$location);
|
|
116
125
|
|
|
@@ -122,7 +131,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
122
131
|
if (node.elements && node.elements[elemName].$location)
|
|
123
132
|
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
124
133
|
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
125
|
-
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
134
|
+
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
126
135
|
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
|
|
127
136
|
});
|
|
128
137
|
copyAnnotations(typeDef, newType);
|
|
@@ -151,7 +160,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
151
160
|
* 1) If it's an anonymous structured type (items.elements || elements)
|
|
152
161
|
* 2) If it's a named type resolve to the final type definition and
|
|
153
162
|
* check if this is a structured type
|
|
154
|
-
* Returns an object that indicates
|
|
163
|
+
* Returns an object that indicates
|
|
155
164
|
* - wether or not the type needs exposure
|
|
156
165
|
* - the elements dictionary that needs to be cloned
|
|
157
166
|
* - the typeDef, either the resolved type def or the node itself
|
|
@@ -1,38 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
forEachDefinition,
|
|
3
|
-
forEachMemberRecursively,
|
|
4
|
-
} = require('../../model/csnUtils');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Return true if 'artifact' has an association type
|
|
8
|
-
function isAssociation(artifact) {
|
|
9
|
-
return (artifact.type === 'cds.Association' || artifact.type === 'Association');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Return true if 'artifact' has a composition type
|
|
13
|
-
function isComposition(artifact) {
|
|
14
|
-
return (artifact.type === 'cds.Composition' || artifact.type === 'Composition')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function isAssociationOrComposition(artifact) {
|
|
18
|
-
return isAssociation(artifact) || isComposition(artifact);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isManagedAssociation(artifact) {
|
|
22
|
-
return artifact.target !== undefined && artifact.on === undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function forEachManagedAssociation(csn, callback, isExternalServiceMember) {
|
|
26
|
-
|
|
27
|
-
forEachDefinition(csn, (def) => {
|
|
28
|
-
forEachMemberRecursively(def, (element) => {
|
|
29
|
-
if (isAssociationOrComposition(element) && !element.on) {
|
|
30
|
-
callback(element)
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
}, { skipArtifact: isExternalServiceMember });
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
'use strict';
|
|
36
2
|
|
|
37
3
|
/**
|
|
38
4
|
* Return the definition name, without the prefixed service name
|
|
@@ -87,12 +53,9 @@ function isLocalizedArtifactInService(artName, services) {
|
|
|
87
53
|
}
|
|
88
54
|
|
|
89
55
|
module.exports = {
|
|
90
|
-
forEachManagedAssociation,
|
|
91
56
|
defNameWithoutServiceOrContextName,
|
|
92
57
|
getServiceOfArtifact,
|
|
93
58
|
isArtifactInService,
|
|
94
59
|
isArtifactInSomeService,
|
|
95
|
-
isAssociationOrComposition,
|
|
96
60
|
isLocalizedArtifactInService,
|
|
97
|
-
isManagedAssociation,
|
|
98
61
|
}
|