@sap/cds-compiler 3.8.2 → 3.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +26 -5
- package/lib/api/.eslintrc.json +3 -2
- package/lib/api/options.js +3 -1
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +28 -19
- package/lib/base/messages.js +6 -1
- package/lib/base/model.js +2 -2
- package/lib/checks/.eslintrc.json +1 -0
- package/lib/checks/actionsFunctions.js +6 -6
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/elements.js +28 -17
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/onConditions.js +11 -6
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/types.js +1 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +3 -2
- package/lib/compiler/assert-consistency.js +7 -2
- package/lib/compiler/base.js +8 -4
- package/lib/compiler/builtins.js +7 -0
- package/lib/compiler/checks.js +73 -6
- package/lib/compiler/define.js +10 -5
- package/lib/compiler/extend.js +910 -1711
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +838 -0
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +2 -2
- package/lib/compiler/propagator.js +20 -8
- package/lib/compiler/resolve.js +3 -3
- package/lib/compiler/shared.js +3 -1
- package/lib/edm/annotations/genericTranslation.js +18 -8
- package/lib/edm/csn2edm.js +14 -14
- package/lib/edm/edm.js +25 -11
- package/lib/edm/edmPreprocessor.js +47 -23
- package/lib/edm/edmUtils.js +37 -9
- package/lib/gen/Dictionary.json +5 -7
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +24 -23
- package/lib/gen/languageLexer.interp +4 -1
- package/lib/gen/languageLexer.js +792 -784
- package/lib/gen/languageLexer.tokens +12 -11
- package/lib/gen/languageParser.js +3564 -3493
- package/lib/json/from-csn.js +28 -6
- package/lib/json/to-csn.js +10 -6
- package/lib/language/antlrParser.js +11 -3
- package/lib/language/genericAntlrParser.js +2 -1
- package/lib/language/language.g4 +14 -3
- package/lib/model/csnRefs.js +10 -5
- package/lib/model/csnUtils.js +41 -76
- package/lib/modelCompare/utils/.eslintrc.json +1 -1
- package/lib/optionProcessor.js +7 -4
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/toCdl.js +244 -168
- package/lib/render/toHdbcds.js +18 -10
- package/lib/render/toSql.js +24 -2
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +11 -6
- package/lib/transform/db/flattening.js +22 -15
- package/lib/transform/db/rewriteCalculatedElements.js +50 -29
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/views.js +1 -1
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/draft/odata.js +3 -4
- package/lib/transform/forOdataNew.js +5 -6
- package/lib/transform/forRelationalDB.js +7 -7
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +6 -6
- package/lib/transform/odata/typesExposure.js +12 -3
- package/lib/transform/odata/utils.js +3 -0
- package/lib/transform/transformUtilsNew.js +11 -26
- package/lib/transform/translateAssocsToJoins.js +9 -9
- package/lib/transform/universalCsn/.eslintrc.json +3 -2
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +6 -4
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,69 @@
|
|
|
7
7
|
Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
|
|
10
|
+
## Version 3.9.4 - 2023-06-07
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- compiler: `USING` empty files were incorrectly marked as "not found".
|
|
15
|
+
- Localized convenience views for projections (not views) did not have references rewritten.
|
|
16
|
+
This only affects CSN, the SQL result was correct.
|
|
17
|
+
- to.edm(x): Render correct EntitySetPath and annotation target path for actions/functions
|
|
18
|
+
with explicit binding parameter.
|
|
19
|
+
|
|
20
|
+
## Version 3.9.2 - 2023-04-27
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Fix crash in core compiler which occurred when CAP was used in a node environments
|
|
25
|
+
where an enumerable property was added to `Array.prototype`.
|
|
26
|
+
- to.edm(x):
|
|
27
|
+
+ Publicly release `@open`.
|
|
28
|
+
+ No `DefaultValue` for `Edm.NavigationProperty`.
|
|
29
|
+
|
|
30
|
+
## Version 3.9.0 - 2023-04-20
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Variables `$valid.from` and `$valid.to` have been added to the compiler.
|
|
35
|
+
They behave the same as `$at.from` and `$at.to`.
|
|
36
|
+
- to.edm(x):
|
|
37
|
+
+ Add `--odata-vocabularies` to pass a dictionary `{ <prefix>: { Alias, Namespace, Uri } }`
|
|
38
|
+
into the EDM generation. `<prefix>` must match the value of `Alias`. Entries are ignored
|
|
39
|
+
if they are incomplete, malformed, redefine an official OASIS/SAP vocabulary or match the name
|
|
40
|
+
of the current service. Annotations of the form `@<prefix>.<annotation>` are added to the API
|
|
41
|
+
without evaluation including an `edm:Reference`. It is in the users responsibility to provide
|
|
42
|
+
a URI that a client can resolve against a valid vocabulary document.
|
|
43
|
+
+ Support annotation `@open` on entity and structured type level to declare the corresponding
|
|
44
|
+
entity/complex type to be `OpenType=true`. If an open structured type is declared closed
|
|
45
|
+
(with a falsy annotation value), the corresponding EDM type is closed as well and suffixed
|
|
46
|
+
with `_closed` (or `_open` vice versa).
|
|
47
|
+
No further checks are performed on possibly open foreign or primary key types nor on eventually
|
|
48
|
+
bucket elements to store the additional data.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- compiler: Parameter references in filters such as `assoc[field < :Param]` are now allowed.
|
|
53
|
+
- In the compiled CSN, sort the non-enumerable `$sources` property
|
|
54
|
+
according to the reversed layered extension order.
|
|
55
|
+
- Update OData vocabulary 'Common', 'ODM', 'UI'.
|
|
56
|
+
- to.cdl: If an identifier contains illegal characters (e.g. newline), we no longer produces
|
|
57
|
+
invalid CDL, but emit an error instead.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- to.edm(x):
|
|
62
|
+
+ Fix spec requirement: "Navigation properties of complex types MUST NOT specify a partner".
|
|
63
|
+
+ Set default target cardinality for unspecified `composition of {}` to `[0..1]`.
|
|
64
|
+
+ Correct referential constraint calculation for `[0..1]` backlink associations.
|
|
65
|
+
- for.hana/for.odata: Reject final unmanaged assoc path step in ON Condition if preceded with `$self`.
|
|
66
|
+
- to.cdl: Parentheses around expressions containing conditions were sometimes missing.
|
|
67
|
+
- to.sql/hdi/hdbcds:
|
|
68
|
+
+ Detect and process calculated elements in functions like `upper`.
|
|
69
|
+
+ Better detection of calculated elements in `.expand`/`.inline`.
|
|
70
|
+
+ Entities with calculated elements sometimes had incorrect types. This happened, for example,
|
|
71
|
+
if they were marked with `@odata.draft.enabled`
|
|
72
|
+
|
|
10
73
|
## Version 3.8.2 - 2023-03-30
|
|
11
74
|
|
|
12
75
|
### Fixed
|
package/bin/cdsc.js
CHANGED
|
@@ -353,8 +353,8 @@ function executeCommandLine( command, options, args ) {
|
|
|
353
353
|
options.odataFormat = 'structured';
|
|
354
354
|
options.odataContainment = true;
|
|
355
355
|
}
|
|
356
|
-
if (options.
|
|
357
|
-
options.
|
|
356
|
+
if (options.odataVocabularies && typeof options.odataVocabularies === 'string')
|
|
357
|
+
options.odataVocabularies = JSON.parse(options.odataVocabularies);
|
|
358
358
|
|
|
359
359
|
const csn = options.directBackend ? model : compactModel(model, options);
|
|
360
360
|
if (options.csn) {
|
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,26 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 3.9.2 - 2023-04-27
|
|
12
|
+
|
|
13
|
+
### Removed `odataOpenType`
|
|
14
|
+
|
|
15
|
+
This feature is now set to production mode.
|
|
16
|
+
|
|
17
|
+
## Version 3.9.0 - 2023-04-XX
|
|
18
|
+
|
|
19
|
+
### Added `calculatedElementsOnWrite`
|
|
20
|
+
|
|
21
|
+
Allows to define calculated elements "on-write" in entities and aspects.
|
|
22
|
+
For example:
|
|
23
|
+
|
|
24
|
+
```cds
|
|
25
|
+
entity E { one: Integer; two = one + 1 stored; };
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
When a row is added to `E`, column `two` will be set automatically
|
|
29
|
+
in databases supporting generated columns.
|
|
30
|
+
|
|
11
31
|
## Version 3.8.0 - 2023-03-27
|
|
12
32
|
|
|
13
33
|
### Added `v4preview`
|
|
@@ -100,11 +120,12 @@ allow lossless, easy to revert actions like adding a column or extending a strin
|
|
|
100
120
|
|
|
101
121
|
### Added `odataOpenType`
|
|
102
122
|
|
|
103
|
-
- to.edm(x): Support annotation `@open` on entity and structured type level to declare the
|
|
104
|
-
be `OpenType=true`. If an open structured type is declared
|
|
105
|
-
is closed as well and suffixed
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
- to.edm(x): Support annotation `@open` on entity and structured type level to declare the
|
|
124
|
+
corresponding entity/complex type to be `OpenType=true`. If an open structured type is declared
|
|
125
|
+
closed (with a falsy annotation value), the corresponding EDM type is closed as well and suffixed
|
|
126
|
+
with `_closed` (or `_open` vice versa).
|
|
127
|
+
No further checks are performed on possibly open foreign or primary key types nor on eventually
|
|
128
|
+
bucket elements to store the additional data.
|
|
108
129
|
|
|
109
130
|
## Version 3.0.0 - 2022-06-23
|
|
110
131
|
|
package/lib/api/.eslintrc.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"root": true,
|
|
3
3
|
"env": {
|
|
4
|
-
"
|
|
4
|
+
"es2022": true,
|
|
5
5
|
"node": true
|
|
6
6
|
},
|
|
7
7
|
// we actually do not extend airbnb-base, as it weakens some eslint:recommended rules
|
|
8
8
|
"extends": ["../../.eslintrc-ydkjsi.json", "plugin:jsdoc/recommended"],
|
|
9
9
|
"parserOptions": {
|
|
10
|
-
"ecmaVersion":
|
|
10
|
+
"ecmaVersion": 2022,
|
|
11
11
|
"sourceType": "script"
|
|
12
12
|
},
|
|
13
13
|
"plugins": [
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"jsdoc/no-undefined-types": "off",
|
|
19
19
|
// eslint-plugin-jsdoc warning
|
|
20
20
|
"jsdoc/require-property": 0,
|
|
21
|
+
"jsdoc/tag-lines": "off",
|
|
21
22
|
// =airbnb, >eslint:
|
|
22
23
|
"max-len": [ "error", {
|
|
23
24
|
"code": 100,
|
package/lib/api/options.js
CHANGED
|
@@ -29,7 +29,9 @@ const publicOptionsNewAPI = [
|
|
|
29
29
|
'variableReplacements',
|
|
30
30
|
'pre2134ReferentialConstraintNames',
|
|
31
31
|
'generatedByComment',
|
|
32
|
+
'betterSqliteSessionVariables',
|
|
32
33
|
// ODATA
|
|
34
|
+
'odataOpenapiHints',
|
|
33
35
|
'odataVersion',
|
|
34
36
|
'odataFormat',
|
|
35
37
|
'odataContainment',
|
|
@@ -38,7 +40,7 @@ const publicOptionsNewAPI = [
|
|
|
38
40
|
'odataProxies',
|
|
39
41
|
'odataXServiceRefs',
|
|
40
42
|
'odataV2PartialConstr',
|
|
41
|
-
'
|
|
43
|
+
'odataVocabularies',
|
|
42
44
|
'service',
|
|
43
45
|
'serviceNames',
|
|
44
46
|
//
|
package/lib/api/validate.js
CHANGED
|
@@ -82,7 +82,7 @@ const validators = {
|
|
|
82
82
|
sqlMapping: generateStringValidator([ 'plain', 'quoted', 'hdbcds' ]),
|
|
83
83
|
odataVersion: generateStringValidator([ 'v2', 'v4' ]),
|
|
84
84
|
odataFormat: generateStringValidator([ 'flat', 'structured' ]),
|
|
85
|
-
|
|
85
|
+
odataVocabularies: {
|
|
86
86
|
validate: val => (typeof val === 'object' && !Array.isArray(val)),
|
|
87
87
|
expected: () => 'type JSON object',
|
|
88
88
|
found: val => `type ${ Array.isArray(val) ? 'JSON array' : typeof val }`,
|
|
@@ -164,17 +164,18 @@ const centralMessages = {
|
|
|
164
164
|
'unexpected-keys-for-composition': { severity: 'Error' }, // TODO: more than 30 chars
|
|
165
165
|
'unmanaged-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing
|
|
166
166
|
'composition-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing and not supported
|
|
167
|
-
|
|
167
|
+
'def-invalid-key-cardinality': { severity: 'Error' },
|
|
168
|
+
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
168
169
|
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
169
|
-
// Published! Used in @
|
|
170
|
+
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
170
171
|
'odata-spec-violation-assoc': { severity: 'Warning' }, // more than 30 chars
|
|
171
|
-
// Published! Used in @
|
|
172
|
+
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
172
173
|
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
173
174
|
'odata-spec-violation-id': { severity: 'Error', configurableFor: true },
|
|
174
175
|
'odata-spec-violation-namespace': { severity: 'Warning' }, // more than 30 chars
|
|
175
|
-
// Published! Used in @
|
|
176
|
+
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
176
177
|
'odata-spec-violation-param': { severity: 'Warning' }, // more than 30 chars
|
|
177
|
-
// Published! Used in @
|
|
178
|
+
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
178
179
|
'odata-spec-violation-returns': { severity: 'Warning' }, // more than 30 chars
|
|
179
180
|
'odata-spec-violation-type': { severity: 'Error', configurableFor: true },
|
|
180
181
|
'odata-spec-violation-type-unknown': { severity: 'Warning' },
|
|
@@ -390,7 +391,7 @@ const centralMessageTexts = {
|
|
|
390
391
|
'syntax-deprecated-value': { // Warning
|
|
391
392
|
std: 'Deprecated representation of the value in property $(PROP)',
|
|
392
393
|
replace: 'Replace value in $(PROP) by $(VALUE)',
|
|
393
|
-
'zero-parens': 'Deprecated CSN
|
|
394
|
+
'zero-parens': 'Deprecated CSN v0.1.0 representation of expressions in parentheses',
|
|
394
395
|
'zero-replace': 'Replace CSN v0.1.0 value in $(PROP) by $(VALUE)',
|
|
395
396
|
},
|
|
396
397
|
'syntax-deprecated-type-ref': {
|
|
@@ -458,8 +459,10 @@ const centralMessageTexts = {
|
|
|
458
459
|
annotation: 'An annotation can\'t include an entity with calculated elements',
|
|
459
460
|
},
|
|
460
461
|
'def-unsupported-calc-elem': {
|
|
461
|
-
std: 'Calculated elements are not supported',
|
|
462
|
-
nested: 'Calculated elements in structures are not supported, yet'
|
|
462
|
+
std: 'Calculated elements are not supported, yet',
|
|
463
|
+
nested: 'Calculated elements in structures are not supported, yet',
|
|
464
|
+
'on-write': 'Calculated elements on-write are not supported, yet',
|
|
465
|
+
'hdbcds': 'Calculated elements on-write are not supported for HDBCDS',
|
|
463
466
|
},
|
|
464
467
|
// 'syntax-unknown-property' (Warning? Better configurable Error)
|
|
465
468
|
|
|
@@ -512,7 +515,7 @@ const centralMessageTexts = {
|
|
|
512
515
|
},
|
|
513
516
|
'ref-expected-element': {
|
|
514
517
|
std: 'Expected element reference',
|
|
515
|
-
magicVar: 'Only elements of
|
|
518
|
+
magicVar: 'Only elements of variable $(ID) can be selected',
|
|
516
519
|
},
|
|
517
520
|
'ref-expected-direct-structure': {
|
|
518
521
|
std: '$(ART) can\'t be extended because it originates from an include',
|
|
@@ -595,14 +598,14 @@ const centralMessageTexts = {
|
|
|
595
598
|
},
|
|
596
599
|
|
|
597
600
|
'def-unexpected-paramview-assoc': {
|
|
598
|
-
std: '
|
|
599
|
-
|
|
600
|
-
target: '
|
|
601
|
+
std: 'unused',
|
|
602
|
+
source: 'Unexpected definition of an association in an entity with parameters',
|
|
603
|
+
target: 'Expected association target to have no parameters',
|
|
601
604
|
},
|
|
602
605
|
'def-unexpected-calcview-assoc': {
|
|
603
|
-
std: '
|
|
604
|
-
'
|
|
605
|
-
'target
|
|
606
|
+
std: 'unused',
|
|
607
|
+
'source': 'Unexpected definition of an association in an entity annotated with $(ANNO)',
|
|
608
|
+
'target': 'Expected association target not to be annotated with $(ANNO)',
|
|
606
609
|
},
|
|
607
610
|
'def-unexpected-key': {
|
|
608
611
|
std: '$(ART) can\'t have additional keys',
|
|
@@ -657,6 +660,12 @@ const centralMessageTexts = {
|
|
|
657
660
|
elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
658
661
|
actions: 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
659
662
|
},
|
|
663
|
+
'ref-invalid-override': {
|
|
664
|
+
std: 'Overridden element of include must not change element structure', // unused
|
|
665
|
+
'new-not-structured': 'Expected element $(NAME) to be structured, because it overrides the included element from $(ART)',
|
|
666
|
+
'old-not-structured': 'Expected element $(NAME) to be scalar, because it overrides the included element from $(ART)',
|
|
667
|
+
missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
|
|
668
|
+
},
|
|
660
669
|
|
|
661
670
|
// TODO: rename to ref-expected-XYZ
|
|
662
671
|
'expected-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
@@ -753,12 +762,12 @@ const centralMessageTexts = {
|
|
|
753
762
|
},
|
|
754
763
|
// version independent messages
|
|
755
764
|
'odata-spec-violation-key-array': {
|
|
756
|
-
std: 'Unexpected array type for
|
|
757
|
-
|
|
765
|
+
std: 'Unexpected array type for primary key $(NAME)',
|
|
766
|
+
assoc: 'Unexpected target cardinality $(VALUE) for primary key $(NAME)',
|
|
758
767
|
},
|
|
759
768
|
'odata-spec-violation-key-null': {
|
|
760
|
-
std: 'Expected key element $(NAME) to be not nullable', //
|
|
761
|
-
scalar: 'Expected key element to be not nullable' //
|
|
769
|
+
std: 'Expected key element $(NAME) to be not nullable', // flat
|
|
770
|
+
scalar: 'Expected key element $(NAME) to be not nullable', // structured
|
|
762
771
|
},
|
|
763
772
|
'odata-spec-violation-key-type': {
|
|
764
773
|
std: 'Unexpected $(TYPE) mapped to $(ID) as type for key element $(NAME)', // structured
|
package/lib/base/messages.js
CHANGED
|
@@ -670,7 +670,7 @@ const paramsTransform = {
|
|
|
670
670
|
alias: quote.double,
|
|
671
671
|
anno,
|
|
672
672
|
annos: transformManyWith( anno ),
|
|
673
|
-
delimited: n => quote.single(
|
|
673
|
+
delimited: n => quote.single( asDelimitedId(n) ),
|
|
674
674
|
file: quote.single,
|
|
675
675
|
option: quote.single,
|
|
676
676
|
prop: quote.single,
|
|
@@ -708,6 +708,11 @@ const paramsTransform = {
|
|
|
708
708
|
version: quote.single, // TODO delete: just use for OData $(VERSION), with version: 2.0
|
|
709
709
|
};
|
|
710
710
|
|
|
711
|
+
function asDelimitedId(id) {
|
|
712
|
+
// Same as in toCdl, but we don't want cyclic dependencies to toCdl.
|
|
713
|
+
return `![${id.replace(/]/g, ']]')}]`;
|
|
714
|
+
}
|
|
715
|
+
|
|
711
716
|
function anno( name ) {
|
|
712
717
|
return (name.charAt(0) === '@') ? quote.double( name ) : quote.double( `@${ name }` );
|
|
713
718
|
}
|
package/lib/base/model.js
CHANGED
|
@@ -29,10 +29,10 @@ const availableBetaFlags = {
|
|
|
29
29
|
ignoreAssocPublishingInUnion: true,
|
|
30
30
|
enableUniversalCsn: true,
|
|
31
31
|
postgres: true,
|
|
32
|
-
aspectWithoutElements: true,
|
|
33
|
-
odataOpenType: true,
|
|
32
|
+
aspectWithoutElements: true, // TODO: do not just remove beta flag - remove elements, too!
|
|
34
33
|
odataTerms: true,
|
|
35
34
|
optionalActionFunctionParameters: true,
|
|
35
|
+
calculatedElementsOnWrite: true,
|
|
36
36
|
// disabled by --beta-mode
|
|
37
37
|
nestedServices: false,
|
|
38
38
|
v4preview: false,
|
|
@@ -67,7 +67,7 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
67
67
|
* @param {string} actKind 'action' or 'function'
|
|
68
68
|
*/
|
|
69
69
|
function checkActionOrFunctionParameter( param, currPath, actKind ) {
|
|
70
|
-
const paramType = param.type ? this.csnUtils.
|
|
70
|
+
const paramType = param.type ? this.csnUtils.getFinalTypeInfo(param.type) : param;
|
|
71
71
|
if (!paramType)
|
|
72
72
|
return; // no type could be resolved
|
|
73
73
|
|
|
@@ -80,7 +80,7 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
if (param.type && this.csnUtils.isAssocOrComposition(param
|
|
83
|
+
if (param.type && this.csnUtils.isAssocOrComposition(param)) {
|
|
84
84
|
this.error(null, currPath, { '#': actKind }, {
|
|
85
85
|
std: 'An association is not allowed as this artifact\'s parameter type', // Not used
|
|
86
86
|
action: 'An association is not allowed as action\'s parameter type',
|
|
@@ -103,11 +103,11 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
103
103
|
* @param {string} actKind 'action' or 'function'
|
|
104
104
|
*/
|
|
105
105
|
function checkReturns( returns, currPath, actKind ) {
|
|
106
|
-
const finalReturnType = returns.type ? this.csnUtils.
|
|
106
|
+
const finalReturnType = returns.type ? this.csnUtils.getFinalTypeInfo(returns.type) : returns;
|
|
107
107
|
if (!finalReturnType)
|
|
108
108
|
return; // no type, e.g. `type of V:calculated`; already an error in `checkTypeOfHasProperType()`
|
|
109
109
|
|
|
110
|
-
if (this.csnUtils.isAssocOrComposition(
|
|
110
|
+
if (this.csnUtils.isAssocOrComposition(returns)) {
|
|
111
111
|
this.error(null, currPath, { '#': actKind },
|
|
112
112
|
{
|
|
113
113
|
std: 'An association is not allowed as this artifact\'s return type', // Not used
|
|
@@ -117,9 +117,9 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (finalReturnType.items) // check array return type
|
|
120
|
-
checkReturns.
|
|
120
|
+
checkReturns.call(this, finalReturnType.items, currPath.concat('items'), actKind);
|
|
121
121
|
else // check if return type is user defined from the current service
|
|
122
|
-
checkUserDefinedType.
|
|
122
|
+
checkUserDefinedType.call(this, finalReturnType, returns.type, currPath);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
@@ -23,7 +23,7 @@ function checkCoreMediaTypeAllowence( member ) {
|
|
|
23
23
|
'cds.hana.CLOB': 1,
|
|
24
24
|
'cds.hana.BINARY': 1,
|
|
25
25
|
};
|
|
26
|
-
if (member['@Core.MediaType'] && member.type && !(this.csnUtils.
|
|
26
|
+
if (member['@Core.MediaType'] && member.type && !(this.csnUtils.getFinalTypeInfo(member.type)?.type in allowedCoreMediaTypes)) {
|
|
27
27
|
this.warning(null, member.$path, { names: [ 'Edm.String', 'Edm.Binary' ] },
|
|
28
28
|
'Element annotated with “@Core.MediaType” should be of a type mapped to $(NAMES)');
|
|
29
29
|
}
|
package/lib/checks/elements.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
forEachMember, forEachMemberRecursively, isBuiltinType, cardinality2str,
|
|
5
|
+
} = require('../model/csnUtils');
|
|
4
6
|
const { isGeoTypeName } = require('../compiler/builtins');
|
|
5
7
|
const { setProp } = require('../base/model');
|
|
6
8
|
// Only to be used with validator.js - a correct `this` value needs to be provided!
|
|
@@ -36,7 +38,7 @@ function checkPrimaryKey( art ) {
|
|
|
36
38
|
*/
|
|
37
39
|
function checkIfPrimaryKeyIsOfGeoType( member, elemFqName, parentIsKey, parentPath ) {
|
|
38
40
|
if (member.key || parentIsKey) {
|
|
39
|
-
const finalBaseType = this.csnUtils.
|
|
41
|
+
const finalBaseType = this.csnUtils.getFinalTypeInfo(member.type);
|
|
40
42
|
if (isGeoTypeName(finalBaseType?.type)) {
|
|
41
43
|
this.error(null, parentPath || member.$path,
|
|
42
44
|
{ type: finalBaseType.type, name: elemFqName },
|
|
@@ -65,10 +67,18 @@ function checkPrimaryKey( art ) {
|
|
|
65
67
|
*/
|
|
66
68
|
function checkIfPrimaryKeyIsArray( member, elemFqName, parentIsKey, parentPath ) {
|
|
67
69
|
if (member.key || parentIsKey) {
|
|
68
|
-
const finalBaseType = this.csnUtils.
|
|
70
|
+
const finalBaseType = this.csnUtils.getFinalTypeInfo(member.type);
|
|
69
71
|
if (member.items || (finalBaseType && finalBaseType.items)) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
let msg = 'std';
|
|
73
|
+
if (member.target)
|
|
74
|
+
msg = this.csnUtils.isComposition(member) ? 'comp' : 'assoc';
|
|
75
|
+
|
|
76
|
+
this.error('def-invalid-key-cardinality', parentPath || member.$path,
|
|
77
|
+
{
|
|
78
|
+
name: elemFqName,
|
|
79
|
+
value: cardinality2str(member, false),
|
|
80
|
+
'#': msg,
|
|
81
|
+
}, 'Array-like type in element $(NAME) can\'t be used as primary key');
|
|
72
82
|
}
|
|
73
83
|
else if (finalBaseType && this.csnUtils.isStructured(finalBaseType) && !finalBaseType.$visited) {
|
|
74
84
|
setProp(finalBaseType, '$visited', true);
|
|
@@ -84,17 +94,17 @@ function checkPrimaryKey( art ) {
|
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
96
|
|
|
97
|
+
|
|
87
98
|
/**
|
|
88
|
-
* Checks virtual
|
|
99
|
+
* Checks virtual elements and throws an error if some is either structured or
|
|
89
100
|
* an association
|
|
90
101
|
*
|
|
91
102
|
* @param {CSN.Element} member Element to be checked
|
|
92
103
|
*/
|
|
93
104
|
function checkVirtualElement( member ) {
|
|
94
105
|
if (member.virtual) {
|
|
95
|
-
if (this.csnUtils.
|
|
96
|
-
this.error(null, member.$path, {}, 'Element can\'t be virtual and an association');
|
|
97
|
-
}
|
|
106
|
+
if (this.csnUtils.isAssocOrComposition(member))
|
|
107
|
+
this.error(null, member.$path, {}, 'Element can\'t be virtual and an association or composition');
|
|
98
108
|
}
|
|
99
109
|
}
|
|
100
110
|
|
|
@@ -107,18 +117,19 @@ function checkVirtualElement( member ) {
|
|
|
107
117
|
*/
|
|
108
118
|
function checkManagedAssoc( art ) {
|
|
109
119
|
forEachMemberRecursively(art, (member) => {
|
|
110
|
-
if (this.csnUtils.isAssocOrComposition(member
|
|
120
|
+
if (this.csnUtils.isAssocOrComposition(member) &&
|
|
111
121
|
!isManagedComposition.bind(this)(member)) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const max = member.cardinality && member.cardinality.max;
|
|
115
|
-
if (max === '*' || Number(max) > 1) {
|
|
122
|
+
const max = member.cardinality?.max ? member.cardinality.max : 1;
|
|
123
|
+
if (max !== 1 && !member.on) {
|
|
116
124
|
const isNoDb = art['@cds.persistence.skip'] || art['@cds.persistence.exists'];
|
|
117
125
|
this.warning(isNoDb ? 'to-many-no-on-noDB' : 'to-many-no-on', member.cardinality ? member.cardinality.$path : member.$path,
|
|
118
|
-
{ '#': this.csnUtils.isComposition(member.type) ? 'cmp' : 'std' },
|
|
119
126
|
{
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
value: cardinality2str(member, false),
|
|
128
|
+
'#': this.csnUtils.isComposition(member) ? 'comp' : 'std',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
std: 'Expected association with target cardinality $(VALUE) to have an ON-condition',
|
|
132
|
+
comp: 'Expected composition with target cardinality $(VALUE) to have an ON-condition',
|
|
122
133
|
});
|
|
123
134
|
}
|
|
124
135
|
}
|
|
@@ -65,7 +65,7 @@ function validateForeignKeys( member, memberName ) {
|
|
|
65
65
|
delete type.$visited;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
else if (mem && !mem.type) {
|
|
68
|
+
else if (mem && !mem.type && this.options.transformation !== 'odata') {
|
|
69
69
|
const variant = member.type === 'cds.Composition' ? 'managedCompForeignKey' : 'managedAssocForeignKey';
|
|
70
70
|
this.error('check-proper-type-of', member.$path, {
|
|
71
71
|
'#': variant, name: memName, art: memberName,
|
|
@@ -27,7 +27,7 @@ function invalidTarget( member ) {
|
|
|
27
27
|
if (!target)
|
|
28
28
|
throw new ModelError(`Expected target ${ mem.target }`);
|
|
29
29
|
if (target.kind !== 'entity') {
|
|
30
|
-
const isAssoc = this.csnUtils.
|
|
30
|
+
const isAssoc = this.csnUtils.getFinalTypeInfo(member.type)?.type !== 'cds.Composition';
|
|
31
31
|
this.error(
|
|
32
32
|
null,
|
|
33
33
|
member.$path,
|
|
@@ -79,23 +79,27 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
79
79
|
// For error messages
|
|
80
80
|
const id = logReady(ref[j]);
|
|
81
81
|
const elemref = { ref };
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
82
|
+
const stepArt = _links[j].art;
|
|
83
|
+
if (stepArt.target &&
|
|
84
|
+
!(stepArt === member ||
|
|
85
|
+
ref[j] === '$self' ||
|
|
86
|
+
ref[j] === '$projection' ||
|
|
87
|
+
(validDollarSelf && j === _links.length - 1))) {
|
|
88
|
+
if (stepArt.on) {
|
|
85
89
|
// It's an unmanaged association - traversal is always forbidden
|
|
86
90
|
this.error('ref-unexpected-navigation', csnPath, { '#': 'unmanaged', id, elemref });
|
|
87
91
|
}
|
|
88
92
|
else {
|
|
89
93
|
// It's a managed association - access of the foreign keys is allowed
|
|
90
94
|
const nextRef = ref[j + 1].id || ref[j + 1];
|
|
91
|
-
if (!
|
|
95
|
+
if (!stepArt.keys.some(r => r.ref[0] === nextRef)) {
|
|
92
96
|
this.error('ref-unexpected-navigation', csnPath, {
|
|
93
97
|
'#': 'std', id, elemref, name: nextRef,
|
|
94
98
|
});
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
|
-
if (
|
|
102
|
+
if (stepArt.virtual)
|
|
99
103
|
this.error(null, csnPath, { id, elemref }, 'Virtual elements can\'t be used in ON-conditions, step $(ID) of path $(ELEMREF)');
|
|
100
104
|
|
|
101
105
|
if (ref[j].where)
|
|
@@ -104,7 +108,8 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
104
108
|
if (ref[j].args)
|
|
105
109
|
this.error(null, csnPath, { id, elemref }, 'ON-conditions must not contain parameters, step $(ID) of path $(ELEMREF)');
|
|
106
110
|
}
|
|
107
|
-
|
|
111
|
+
|
|
112
|
+
if (_art && !($scope === '$self' && ref.length === 1)) {
|
|
108
113
|
const type = resolveArtifactType.call(this, _art);
|
|
109
114
|
if (type) {
|
|
110
115
|
// For error messages
|
|
@@ -84,7 +84,7 @@ function checkQueryForNoDBArtifacts( query ) {
|
|
|
84
84
|
const isJoinRelevant = (assoc, nextStep) => {
|
|
85
85
|
if (!assoc.keys)
|
|
86
86
|
return true;
|
|
87
|
-
const isExposedColumnAssocOrComposition = this.csnUtils.isAssocOrComposition(obj._art
|
|
87
|
+
const isExposedColumnAssocOrComposition = this.csnUtils.isAssocOrComposition(obj._art);
|
|
88
88
|
return !assoc.keys
|
|
89
89
|
.some(fk => fk.ref[0] === nextStep && !isExposedColumnAssocOrComposition);
|
|
90
90
|
};
|
package/lib/checks/types.js
CHANGED
|
@@ -130,7 +130,7 @@ function checkTypeOfHasProperType( artOrElement, name, model, error, path, deriv
|
|
|
130
130
|
if (!artOrElement.type)
|
|
131
131
|
return;
|
|
132
132
|
|
|
133
|
-
const typeOfType = this.csnUtils.
|
|
133
|
+
const typeOfType = this.csnUtils.getFinalTypeInfo(artOrElement.type);
|
|
134
134
|
|
|
135
135
|
if (typeOfType === null) {
|
|
136
136
|
if (artOrElement.type.ref && this?.options?.transformation !== 'odata') {
|
package/lib/checks/utils.js
CHANGED
|
@@ -59,7 +59,7 @@ function otherSideIsExpandableStructure( on, startIndex ) {
|
|
|
59
59
|
*/
|
|
60
60
|
function resolveArtifactType( art ) {
|
|
61
61
|
if (art && art.type && !isBuiltinType(art.type))
|
|
62
|
-
return this.csnUtils.
|
|
62
|
+
return this.csnUtils.getFinalTypeInfo(art.type);
|
|
63
63
|
|
|
64
64
|
return art;
|
|
65
65
|
}
|
package/lib/checks/validator.js
CHANGED
|
@@ -57,10 +57,12 @@ const forRelationalDBMemberValidators
|
|
|
57
57
|
checkSqlAnnotationOnElement,
|
|
58
58
|
// no temporal annotations on calc elements
|
|
59
59
|
rejectAnnotationsOnCalcElement,
|
|
60
|
+
checkElementTypeDefinitionHasType,
|
|
60
61
|
];
|
|
61
62
|
|
|
62
63
|
const forRelationalDBArtifactValidators
|
|
63
64
|
= [
|
|
65
|
+
checkPrimaryKey,
|
|
64
66
|
// @cds.persistence has no impact on odata
|
|
65
67
|
validateCdsPersistenceAnnotation,
|
|
66
68
|
// virtual items are not persisted on the db
|
|
@@ -108,12 +110,11 @@ const forOdataQueryValidators = [];
|
|
|
108
110
|
const commonMemberValidators
|
|
109
111
|
= [ validateOnCondition, validateForeignKeys,
|
|
110
112
|
validateAssociationsInItems, checkForInvalidTarget,
|
|
111
|
-
checkVirtualElement
|
|
113
|
+
checkVirtualElement ];
|
|
112
114
|
|
|
113
115
|
// TODO: checkManagedAssoc is a forEachMemberRecursively!
|
|
114
116
|
const commonArtifactValidators = [
|
|
115
117
|
checkTypeDefinitionHasType,
|
|
116
|
-
checkPrimaryKey,
|
|
117
118
|
checkManagedAssoc,
|
|
118
119
|
checkRecursiveTypeUsage,
|
|
119
120
|
];
|
|
@@ -100,12 +100,13 @@ function assertConsistency( model, stage ) {
|
|
|
100
100
|
'$builtins',
|
|
101
101
|
'$internal',
|
|
102
102
|
'$compositionTargets',
|
|
103
|
-
'$
|
|
103
|
+
'$collectedExtensions',
|
|
104
104
|
'_entities', '$entity',
|
|
105
105
|
'$blocks',
|
|
106
106
|
'$messageFunctions',
|
|
107
107
|
'$functions',
|
|
108
108
|
'$volatileFunctions',
|
|
109
|
+
'_sortedSources',
|
|
109
110
|
],
|
|
110
111
|
},
|
|
111
112
|
':parser': { // top-level from parser
|
|
@@ -139,6 +140,7 @@ function assertConsistency( model, stage ) {
|
|
|
139
140
|
},
|
|
140
141
|
},
|
|
141
142
|
sources: { test: isDictionary( isObject ) },
|
|
143
|
+
_sortedSources: { test: isArray( isObject ) },
|
|
142
144
|
file: { test: isString },
|
|
143
145
|
dirname: { test: isString }, // TODO: really necessary?
|
|
144
146
|
realname: { test: isString }, // TODO: really necessary?
|
|
@@ -401,6 +403,8 @@ function assertConsistency( model, stage ) {
|
|
|
401
403
|
'elements', 'items', 'enum',
|
|
402
404
|
// CSN parser may let these properties slip through to XSN, even if input is invalid.
|
|
403
405
|
'args', 'op', 'func', 'suffix',
|
|
406
|
+
// calculated elements on-write
|
|
407
|
+
'stored',
|
|
404
408
|
],
|
|
405
409
|
|
|
406
410
|
kind: true,
|
|
@@ -471,6 +475,7 @@ function assertConsistency( model, stage ) {
|
|
|
471
475
|
op: { test: locationVal( isString ) },
|
|
472
476
|
join: { test: locationVal( isString ) },
|
|
473
477
|
quantifier: { test: locationVal( isString ) },
|
|
478
|
+
stored: { test: locationVal( isBoolean ) },
|
|
474
479
|
// preliminary -----------------------------------------------------------
|
|
475
480
|
doc: {
|
|
476
481
|
kind: true,
|
|
@@ -608,7 +613,7 @@ function assertConsistency( model, stage ) {
|
|
|
608
613
|
$entity: { kind: true, test: TODO },
|
|
609
614
|
_entities: { test: TODO },
|
|
610
615
|
$compositionTargets: { test: isDictionary( isBoolean ) },
|
|
611
|
-
$
|
|
616
|
+
$collectedExtensions: { test: TODO },
|
|
612
617
|
_upperAspects: { kind: [ 'type', 'entity' ], test: isArray( TODO ) },
|
|
613
618
|
|
|
614
619
|
// for implicit redirection - direct and indirect query sources of simple
|