@sap/cds-compiler 2.15.2 → 3.0.2
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 +66 -1590
- package/bin/cdsc.js +42 -46
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -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 +312 -143
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +80 -24
- package/lib/base/messages.js +103 -52
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +7 -5
- 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 +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +127 -10
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +63 -12
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +16 -14
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +194 -222
- package/lib/compiler/shared.js +56 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +7 -2
- package/lib/edm/annotations/genericTranslation.js +60 -6
- package/lib/edm/annotations/preprocessAnnotations.js +10 -11
- package/lib/edm/csn2edm.js +39 -41
- package/lib/edm/edm.js +22 -15
- package/lib/edm/edmPreprocessor.js +66 -69
- package/lib/edm/edmUtils.js +12 -62
- package/lib/gen/Dictionary.json +8 -6
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20717 -22376
- package/lib/json/from-csn.js +73 -68
- package/lib/json/to-csn.js +13 -10
- 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 +333 -259
- package/lib/language/language.g4 +600 -645
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +2 -1
- package/lib/model/csnUtils.js +183 -285
- package/lib/model/revealInternalProperties.js +32 -9
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +64 -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 +334 -339
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +60 -54
- package/lib/render/utils/common.js +15 -1
- 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/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- 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 +19 -22
- package/lib/transform/forOdataNew.js +13 -15
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +11 -9
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +6 -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/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/fix_antlr4-8_warning.js +0 -56
|
@@ -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
|
|
|
@@ -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,7 +148,9 @@ 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 &&
|
|
151
|
+
if (finalType.items &&
|
|
152
|
+
(isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
|
|
153
|
+
{
|
|
152
154
|
if (!isArtifactInService(node.type, currService) || !isV4) {
|
|
153
155
|
node.items = finalType.items;
|
|
154
156
|
delete node.type;
|
|
@@ -184,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
184
186
|
function isUserDefinedBuiltinFromTheCurrService(node, defName) {
|
|
185
187
|
// in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
|
|
186
188
|
// a builtin from the service - do not expand to the final base type
|
|
187
|
-
let finalBaseType = csnUtils.
|
|
188
|
-
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.
|
|
189
|
+
let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
|
|
190
|
+
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
|
|
189
191
|
const currService = csnUtils.getServiceName(defName);
|
|
190
192
|
return node.type && !node.type.ref
|
|
191
193
|
&& isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
|
|
@@ -14,7 +14,7 @@ const { copyAnnotations } = require('../../model/csnUtils');
|
|
|
14
14
|
function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
|
|
15
15
|
const { error } = message;
|
|
16
16
|
// are we working with OData proxies or cross-service refs
|
|
17
|
-
const isMultiSchema = options.
|
|
17
|
+
const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.odataXServiceRefs);
|
|
18
18
|
// collect in this variable all the newly exposed types
|
|
19
19
|
const schemas = Object.create(null);
|
|
20
20
|
const exposedTypes = Object.create(null);
|
|
@@ -122,7 +122,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
122
122
|
if (node.elements && node.elements[elemName].$location)
|
|
123
123
|
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
124
124
|
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
125
|
-
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
125
|
+
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
126
126
|
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
|
|
127
127
|
});
|
|
128
128
|
copyAnnotations(typeDef, newType);
|
|
@@ -151,7 +151,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
151
151
|
* 1) If it's an anonymous structured type (items.elements || elements)
|
|
152
152
|
* 2) If it's a named type resolve to the final type definition and
|
|
153
153
|
* check if this is a structured type
|
|
154
|
-
* Returns an object that indicates
|
|
154
|
+
* Returns an object that indicates
|
|
155
155
|
* - wether or not the type needs exposure
|
|
156
156
|
* - the elements dictionary that needs to be cloned
|
|
157
157
|
* - 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
|
}
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const { hasErrors, makeMessageFunction } = require('../base/messages');
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
|
-
const { csnRefs } = require('../model/csnRefs');
|
|
10
9
|
|
|
11
10
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const { cloneCsnNonDict, cloneCsnDictionary, getUtils
|
|
11
|
+
const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
|
|
12
|
+
const { typeParameters, isBuiltinType } = require('../compiler/builtins');
|
|
13
13
|
const { ModelError } = require("../base/error");
|
|
14
14
|
const { forEach } = require('../utils/objectUtils');
|
|
15
15
|
|
|
@@ -20,20 +20,18 @@ const { forEach } = require('../utils/objectUtils');
|
|
|
20
20
|
// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
|
|
21
21
|
function getTransformers(model, options, pathDelimiter = '_') {
|
|
22
22
|
const { error, warning, info } = makeMessageFunction(model, options);
|
|
23
|
+
const csnUtils = getUtils(model);
|
|
23
24
|
const {
|
|
24
25
|
getCsnDef,
|
|
25
|
-
|
|
26
|
+
getFinalBaseTypeWithProps,
|
|
26
27
|
hasAnnotationValue,
|
|
27
28
|
inspectRef,
|
|
28
29
|
isStructured,
|
|
29
|
-
} = getUtils(model);
|
|
30
|
-
|
|
31
|
-
const {
|
|
32
30
|
effectiveType,
|
|
33
|
-
} =
|
|
34
|
-
|
|
31
|
+
} = csnUtils;
|
|
35
32
|
|
|
36
33
|
return {
|
|
34
|
+
csnUtils,
|
|
37
35
|
resolvePath,
|
|
38
36
|
flattenPath,
|
|
39
37
|
addDefaultTypeFacets,
|
|
@@ -45,7 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
45
43
|
copyTypeProperties,
|
|
46
44
|
isAssociationOperand,
|
|
47
45
|
isDollarSelfOrProjectionOperand,
|
|
48
|
-
getFinalBaseType,
|
|
49
46
|
createExposingProjection,
|
|
50
47
|
createAndAddDraftAdminDataProjection,
|
|
51
48
|
createScalarElement,
|
|
@@ -243,21 +240,19 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
243
240
|
// for type of 'x' -> elem.type is an object, not a string -> use directly
|
|
244
241
|
let elemType;
|
|
245
242
|
if (!elem.elements) // structures do not have final base type
|
|
246
|
-
elemType =
|
|
243
|
+
elemType = getFinalBaseTypeWithProps(elem.type);
|
|
247
244
|
|
|
248
245
|
const struct = elemType ? elemType.elements : elem.elements;
|
|
249
246
|
|
|
250
247
|
// Collect all child elements (recursively) into 'result'
|
|
251
248
|
// TODO: Do not report collisions in the generated elements here, but instead
|
|
252
|
-
//
|
|
249
|
+
// leave that work to the receiver of this result
|
|
253
250
|
let result = Object.create(null);
|
|
254
251
|
const addGeneratedFlattenedElement = (e, eName) => {
|
|
255
|
-
if(result[eName])
|
|
256
|
-
error(
|
|
257
|
-
|
|
258
|
-
} else {
|
|
252
|
+
if (result[eName])
|
|
253
|
+
error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
|
|
254
|
+
else
|
|
259
255
|
result[eName] = e;
|
|
260
|
-
}
|
|
261
256
|
}
|
|
262
257
|
forEach(struct, (childName, childElem) => {
|
|
263
258
|
if (isStructured(childElem)) {
|
|
@@ -287,7 +282,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
287
282
|
|
|
288
283
|
// Fix all collected flat elements (names, annotations, properties, origin ..)
|
|
289
284
|
forEach(result, (name, flatElem) => {
|
|
290
|
-
// Copy annotations from struct (not overwriting, because deep annotations should have precedence)
|
|
285
|
+
// Copy annotations from struct (not overwriting, because deep annotations should have precedence).
|
|
286
|
+
// Attention:
|
|
287
|
+
// This has historic reasons. We don't copy doc-comments because copying annotations
|
|
288
|
+
// is questionable to begin with. Only selected annotations should have been copied,
|
|
289
|
+
// if at all.
|
|
291
290
|
copyAnnotations(elem, flatElem, false);
|
|
292
291
|
// Copy selected type properties
|
|
293
292
|
const props = ['key', 'virtual', 'masked', 'viaAll'];
|
|
@@ -395,78 +394,65 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
395
394
|
}
|
|
396
395
|
|
|
397
396
|
/**
|
|
398
|
-
* Replace the type of '
|
|
399
|
-
*
|
|
400
|
-
* Similar with associations and compositions (we probably need a _baseType link)
|
|
397
|
+
* Replace the type of 'nodeWithType' with its final base type, i.e. copy relevant type properties and
|
|
398
|
+
* set the `type` property to the builtin if scalar or delete it if structured/arrayed.
|
|
401
399
|
*
|
|
402
|
-
* @param {
|
|
400
|
+
* @param {object} nodeWithType
|
|
403
401
|
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
404
402
|
* @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
|
|
405
|
-
* @returns {void}
|
|
406
403
|
*/
|
|
407
|
-
function toFinalBaseType(
|
|
408
|
-
|
|
409
|
-
if (!
|
|
410
|
-
// In case of a ref -> Follow the ref
|
|
411
|
-
if (node.type && node.type.ref) {
|
|
412
|
-
const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
|
|
413
|
-
if(finalBaseType === null)
|
|
414
|
-
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
415
|
-
if(finalBaseType.elements) {
|
|
416
|
-
// This changes the order - to be discussed!
|
|
417
|
-
node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
|
|
418
|
-
delete node.type; // delete the type reference as edm processing does not expect it
|
|
419
|
-
} else if(finalBaseType.items) {
|
|
420
|
-
// This changes the order - to be discussed!
|
|
421
|
-
node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
|
|
422
|
-
delete node.type;
|
|
423
|
-
} else {
|
|
424
|
-
node.type=finalBaseType;
|
|
425
|
-
}
|
|
404
|
+
function toFinalBaseType(nodeWithType, resolved = new WeakMap(), keepLocalized = false) {
|
|
405
|
+
const type = nodeWithType?.type;
|
|
406
|
+
if (!type || nodeWithType.elements || nodeWithType.items || resolved.has(nodeWithType)) {
|
|
426
407
|
return;
|
|
427
408
|
}
|
|
428
|
-
//
|
|
429
|
-
|
|
409
|
+
// The caller may use `{ art }` syntax for `{ ref }` objects, but we only use
|
|
410
|
+
// it to indicate that an artifact has been processed.
|
|
411
|
+
resolved.set(nodeWithType, nodeWithType);
|
|
430
412
|
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (typeDef.items || typeDef.elements) {
|
|
435
|
-
// cloneCsn only works correctly if we start "from the top"
|
|
436
|
-
const cloneTypeDef = cloneCsnNonDict(typeDef, options);
|
|
437
|
-
// With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
|
|
438
|
-
if(typeDef.items) {
|
|
439
|
-
delete node.type;
|
|
440
|
-
if(!node.items)
|
|
441
|
-
Object.assign(node, {items: cloneTypeDef.items});
|
|
442
|
-
}
|
|
443
|
-
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
444
|
-
if(!typeDef.items)
|
|
445
|
-
delete node.type;
|
|
446
|
-
if(!node.elements)
|
|
447
|
-
Object.assign(node, {elements: cloneTypeDef.elements});
|
|
448
|
-
}
|
|
413
|
+
// Nothing to copy from builtin.
|
|
414
|
+
if (typeof type === 'string' && isBuiltinType(type))
|
|
415
|
+
return;
|
|
449
416
|
|
|
417
|
+
let typeRef = null;
|
|
418
|
+
if (resolved.has(type)) {
|
|
419
|
+
typeRef = resolved.get(type)?.art
|
|
420
|
+
// The cached entry may not be resolved, yet.
|
|
421
|
+
if (typeRef.type && !isBuiltinType(typeRef.type))
|
|
422
|
+
typeRef = getFinalBaseTypeWithProps(typeRef.type);
|
|
423
|
+
} else {
|
|
424
|
+
typeRef = getFinalBaseTypeWithProps(type);
|
|
425
|
+
}
|
|
450
426
|
|
|
427
|
+
if (typeRef.elements || typeRef.items) {
|
|
428
|
+
// Copy elements/items and we're finished. No need to look up actual base type,
|
|
429
|
+
// since it must also be structured and must contain at least as many elements,
|
|
430
|
+
// if not more (in client style CSN).
|
|
431
|
+
if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
432
|
+
nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
|
|
433
|
+
delete nodeWithType.type;
|
|
434
|
+
}
|
|
435
|
+
if (typeRef.items) {
|
|
436
|
+
nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
|
|
437
|
+
delete nodeWithType.type;
|
|
438
|
+
}
|
|
451
439
|
return;
|
|
452
440
|
}
|
|
453
|
-
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
|
|
441
|
+
|
|
442
|
+
if (typeRef.enum && nodeWithType.enum === undefined)
|
|
443
|
+
nodeWithType.enum = cloneCsnDictionary(typeRef.enum, options);
|
|
444
|
+
|
|
445
|
+
// Copy type and type arguments (+ localized)
|
|
446
|
+
|
|
447
|
+
for (const param of typeParameters.list) {
|
|
448
|
+
if (nodeWithType[param] === undefined && typeRef[param] !== undefined &&!typeRef.$default) {
|
|
449
|
+
nodeWithType[param] = typeRef[param];
|
|
450
|
+
}
|
|
457
451
|
}
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
if (
|
|
461
|
-
|
|
462
|
-
if (node.scale === undefined && typeDef.scale !== undefined)
|
|
463
|
-
Object.assign(node, { scale: typeDef.scale });
|
|
464
|
-
if (node.srid === undefined && typeDef.srid !== undefined)
|
|
465
|
-
Object.assign(node, { srid: typeDef.srid });
|
|
466
|
-
if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
|
|
467
|
-
Object.assign(node, { localized: typeDef.localized });
|
|
468
|
-
node.type = typeDef.type;
|
|
469
|
-
toFinalBaseType(node);
|
|
452
|
+
if (keepLocalized && nodeWithType.localized === undefined && typeRef.localized !== undefined)
|
|
453
|
+
nodeWithType.localized = typeRef.localized;
|
|
454
|
+
if (typeRef.type)
|
|
455
|
+
nodeWithType.type = typeRef.type;
|
|
470
456
|
}
|
|
471
457
|
|
|
472
458
|
// Return a full projection 'projectionId' of artifact 'art' for exposure in 'service'.
|
|
@@ -57,10 +57,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
57
57
|
const options = model.options || inputOptions;
|
|
58
58
|
|
|
59
59
|
// create JOINs for foreign key paths
|
|
60
|
-
const noJoinForFK = options.forHana ? !options.
|
|
60
|
+
const noJoinForFK = options.forHana ? !options.joinfk : true;
|
|
61
61
|
|
|
62
62
|
// Note: This is called from the 'forHana' transformations, so it is controlled by its options)
|
|
63
|
-
const pathDelimiter = (options.forHana && options.
|
|
63
|
+
const pathDelimiter = (options.forHana && options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
64
64
|
|
|
65
65
|
forEachDefinition(model, prepareAssociations);
|
|
66
66
|
forEachDefinition(model, transformQueries);
|
|
@@ -1818,6 +1818,10 @@ function walkPath(node, env)
|
|
|
1818
1818
|
// or that are parameters ($parameters or escaped paths (':')
|
|
1819
1819
|
//path.length && path[ path.length-1 ]._artifact
|
|
1820
1820
|
const art = path && path.length && path[path.length-1]._artifact;
|
|
1821
|
+
|
|
1822
|
+
// regardless of the position in the query, ignore paths that have virtual path steps
|
|
1823
|
+
if(art && path.some(ps => ps._artifact && ps._artifact.virtual && ps._artifact.virtual.val))
|
|
1824
|
+
return path;
|
|
1821
1825
|
if(art && !internalArtifactKinds.includes(art.kind))
|
|
1822
1826
|
{
|
|
1823
1827
|
if(env.callback)
|
|
@@ -12,7 +12,7 @@ const { setAnnotationIfNotDefined } = require('./utils');
|
|
|
12
12
|
*/
|
|
13
13
|
function setCoreComputedOnViews(csn) {
|
|
14
14
|
const {
|
|
15
|
-
artifactRef, getColumn, getElement,
|
|
15
|
+
artifactRef, getColumn, getElement, getOrigin,
|
|
16
16
|
} = getUtils(csn, 'init-all');
|
|
17
17
|
|
|
18
18
|
forEachDefinition(csn, (artifact) => {
|
|
@@ -57,7 +57,15 @@ function setCoreComputedOnViews(csn) {
|
|
|
57
57
|
const column = getColumn(element);
|
|
58
58
|
if (column)
|
|
59
59
|
return column;
|
|
60
|
-
|
|
60
|
+
const from = getElementFromFrom(name, base.from);
|
|
61
|
+
if (from)
|
|
62
|
+
return from;
|
|
63
|
+
// For .expand/.inline, we can find it via origin
|
|
64
|
+
// Although I would have expected to find it via getColumn...
|
|
65
|
+
const origin = getOrigin(element);
|
|
66
|
+
if (origin)
|
|
67
|
+
return origin;
|
|
68
|
+
throw new Error(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/**
|
|
@@ -87,10 +95,7 @@ function setCoreComputedOnViews(csn) {
|
|
|
87
95
|
return getElementFromFrom(name, base.SET.args[0]);
|
|
88
96
|
}
|
|
89
97
|
else if (base.args && base.join) {
|
|
90
|
-
|
|
91
|
-
if (!result)
|
|
92
|
-
throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
|
|
93
|
-
return result;
|
|
98
|
+
return checkJoinSources(base.args, name);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
throw new Error(JSON.stringify(base));
|