@sap/cds-compiler 4.0.2 → 4.2.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -52,6 +52,7 @@ const centralMessages = {
|
|
|
52
52
|
'anno-unstable-array': { severity: 'Warning' },
|
|
53
53
|
'anno-invalid-sql-element': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
54
54
|
'anno-invalid-sql-struct': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
55
|
+
'anno-invalid-sql-assoc': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
55
56
|
'anno-invalid-sql-calc': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
56
57
|
'anno-invalid-sql-kind': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
57
58
|
'anno-invalid-sql-view': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
@@ -62,47 +63,38 @@ const centralMessages = {
|
|
|
62
63
|
'anno-undefined-element': { severity: 'Warning' },
|
|
63
64
|
'anno-undefined-param': { severity: 'Warning' },
|
|
64
65
|
'anno-unexpected-ellipsis': { severity: 'Error', configurableFor: 'deprecated' },
|
|
65
|
-
|
|
66
|
-
'args-expecting-named': { severity: 'Error' },
|
|
67
|
-
'args-no-params': { severity: 'Error' },
|
|
68
|
-
'args-undefined-param': { severity: 'Error' },
|
|
66
|
+
'anno-unexpected-localized-skip': { severity: 'Error', configurableFor: true },
|
|
69
67
|
|
|
70
68
|
'name-invalid-dollar-alias': { severity: 'Error', configurableFor: true },
|
|
71
69
|
|
|
72
70
|
'type-invalid-items': { severity: 'Error' }, // not supported yet
|
|
73
71
|
'assoc-as-type': { severity: 'Error' }, // TODO: allow more, but not all
|
|
72
|
+
'def-unexpected-nested-proj': { severity: 'Error', configurableFor: 'v4' },
|
|
74
73
|
'def-unexpected-paramview-assoc': { severity: 'Error' },
|
|
75
74
|
'def-unexpected-calcview-assoc': { severity: 'Error' },
|
|
76
75
|
'chained-array-of': { severity: 'Error' },
|
|
77
|
-
'
|
|
76
|
+
'def-missing-type': { severity: 'Error', configurableFor: [ 'compile' ] },
|
|
78
77
|
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename' ] },
|
|
79
78
|
|
|
80
79
|
'def-duplicate-autoexposed': { severity: 'Error' },
|
|
81
80
|
'def-unexpected-default': { severity: 'Error', configurableFor: 'test' },
|
|
82
81
|
|
|
83
|
-
'expr-
|
|
82
|
+
'expr-unexpected-filter': { severity: 'Error' },
|
|
84
83
|
|
|
85
84
|
'empty-type': { severity: 'Info' }, // only still an error in old transformers
|
|
86
85
|
|
|
87
86
|
'ref-deprecated-orderby': { severity: 'Error', configurableFor: true },
|
|
88
|
-
'ref-
|
|
89
|
-
'ref-sloppy-type': { severity: 'Error' },
|
|
87
|
+
'ref-invalid-type': { severity: 'Error' },
|
|
90
88
|
'ref-unexpected-self': { severity: 'Error' },
|
|
91
|
-
'ref-
|
|
89
|
+
'ref-invalid-include': { severity: 'Error' },
|
|
92
90
|
'type-unexpected-typeof': { severity: 'Error' },
|
|
93
91
|
'type-ignoring-argument': { severity: 'Error', configurableFor: true },
|
|
94
92
|
'type-expected-builtin': { severity: 'Error', configurableFor: true },
|
|
95
93
|
'type-expecting-service-target': { severity: 'Error', configurableFor: true },
|
|
96
|
-
'ref-expecting-action-param-type': { severity: 'Error' },
|
|
97
|
-
'ref-sloppy-actionparam-type': { severity: 'Error' },
|
|
98
|
-
'ref-expecting-event-type': { severity: 'Error' }, // TODO: Test coverage
|
|
99
|
-
'ref-sloppy-event-type': { severity: 'Error' },
|
|
100
|
-
'ref-expecting-struct': { severity: 'Error' },
|
|
101
94
|
'ref-expecting-const': { severity: 'Error' },
|
|
102
|
-
'ref-
|
|
103
|
-
'ref-
|
|
104
|
-
'ref-
|
|
105
|
-
'ref-sloppy-target': { severity: 'Warning' },
|
|
95
|
+
'ref-invalid-source': { severity: 'Error' },
|
|
96
|
+
'ref-invalid-target': { severity: 'Error' },
|
|
97
|
+
'ref-sloppy-target': { severity: 'Error', configurableFor: 'v4' },
|
|
106
98
|
|
|
107
99
|
'extend-repeated-intralayer': { severity: 'Warning' },
|
|
108
100
|
'extend-unrelated-layer': { severity: 'Info' },
|
|
@@ -123,6 +115,7 @@ const centralMessages = {
|
|
|
123
115
|
'type-ambiguous-target': { severity: 'Warning' },
|
|
124
116
|
|
|
125
117
|
'ref-unexpected-autoexposed': { severity: 'Error' },
|
|
118
|
+
'ref-unexpected-many-navigation': { severity: 'Error' },
|
|
126
119
|
// Published! Used in @sap/cds-lsp; if renamed, add to oldMessageIds and contact colleagues
|
|
127
120
|
'ref-undefined-art': { severity: 'Error' },
|
|
128
121
|
'ref-undefined-def': { severity: 'Error' },
|
|
@@ -166,15 +159,13 @@ const centralMessages = {
|
|
|
166
159
|
'syntax-unexpected-sql-clause': { severity: 'Error' }, // TODO: configurableFor:'tests'?
|
|
167
160
|
|
|
168
161
|
'type-managed-composition': { severity: 'Error' },
|
|
169
|
-
'type-unsupported-precision-change': { severity: 'Error'},
|
|
162
|
+
'type-unsupported-precision-change': { severity: 'Error' },
|
|
163
|
+
'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
|
|
170
164
|
|
|
171
165
|
'def-missing-element': { severity: 'Error' },
|
|
172
166
|
|
|
173
167
|
'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
|
|
174
168
|
|
|
175
|
-
'unexpected-keys-for-composition': { severity: 'Error' }, // TODO: more than 30 chars
|
|
176
|
-
'unmanaged-as-key': { severity: 'Error' }, // is confusing
|
|
177
|
-
'composition-as-key': { severity: 'Error' }, // is confusing and not supported
|
|
178
169
|
'def-invalid-key-cardinality': { severity: 'Error' },
|
|
179
170
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
180
171
|
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
@@ -213,6 +204,8 @@ const oldMessageIds = createDict({
|
|
|
213
204
|
'old-anno-duplicate': 'anno-duplicate', // Example
|
|
214
205
|
'assoc-in-array': 'type-invalid-items',
|
|
215
206
|
'duplicate-autoexposed': 'def-duplicate-autoexposed',
|
|
207
|
+
'expr-no-filter': 'expr-unexpected-filter',
|
|
208
|
+
'check-proper-type': 'def-missing-type',
|
|
216
209
|
});
|
|
217
210
|
|
|
218
211
|
// Set up the old-to-new message ID mapping in the message registry.
|
|
@@ -239,6 +232,8 @@ const centralMessageTexts = {
|
|
|
239
232
|
std: 'Invalid option $(NAME)!',
|
|
240
233
|
deprecated: 'Option $(NAME) is no longer supported! Use latest API options instead',
|
|
241
234
|
magicVars: 'Option $(PROP) is no longer supported! Use $(OTHERPROP) instead. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
|
|
235
|
+
value: 'Expected option $(PROP) to have $(VALUE). Found: $(OTHERVALUE)',
|
|
236
|
+
type: 'Expected option $(OPTION) to be of type $(VALUE). Found: $(OTHERVALUE)',
|
|
242
237
|
},
|
|
243
238
|
|
|
244
239
|
'api-invalid-variable-replacement': {
|
|
@@ -248,6 +243,22 @@ const centralMessageTexts = {
|
|
|
248
243
|
'noDollar': 'Option $(OPTION) does not know $(NAME). Did you forget a leading $(CODE)?'
|
|
249
244
|
},
|
|
250
245
|
|
|
246
|
+
'api-invalid-combination': {
|
|
247
|
+
std: 'Invalid option combination found: $(OPTION) and $(PROP)', // unused
|
|
248
|
+
'valid-structured': 'Structured OData is only supported with OData version v4',
|
|
249
|
+
'sql-dialect-and-naming': 'sqlDialect $(NAME) can\'t be combined with sqlMapping $(PROP)',
|
|
250
|
+
'sql-dialect-and-localized': 'Option $(OPTION) can\'t be combined with SQL dialect $(VALUE) or the to.hdi()/to.hdbcds() backend',
|
|
251
|
+
},
|
|
252
|
+
'api-unexpected-combination': {
|
|
253
|
+
std: 'Unexpected option combination: $(OPTION) and $(PROP)', // unused
|
|
254
|
+
'beta-no-test':'Option $(OPTION) was used. This option should not be used in productive scenarios!',
|
|
255
|
+
},
|
|
256
|
+
'api-invalid-lookup-dir': {
|
|
257
|
+
std: '',
|
|
258
|
+
slash: 'Expected directory $(VALUE) in option $(OPTION) to end with $(OTHERVALUE)',
|
|
259
|
+
relative: 'Expected directory $(VALUE) in option $(OPTION) to not start with $(OTHERVALUE)',
|
|
260
|
+
},
|
|
261
|
+
|
|
251
262
|
'anno-duplicate': {
|
|
252
263
|
std: 'Duplicate assignment with $(ANNO)',
|
|
253
264
|
doc: 'Duplicate assignment with a doc comment',
|
|
@@ -511,7 +522,12 @@ const centralMessageTexts = {
|
|
|
511
522
|
// Messages for erroneous references -----------------------------------------
|
|
512
523
|
// location at erroneous reference (if possible)
|
|
513
524
|
'ref-deprecated-orderby': 'Replace source element reference $(ID) by $(NEWCODE); auto-corrected',
|
|
514
|
-
'ref-unexpected-self':
|
|
525
|
+
'ref-unexpected-self': {
|
|
526
|
+
std: 'Unexpected $(ID) reference; is valid only in ON-conditions of unmanaged associations',
|
|
527
|
+
on: 'Unexpected $(ID) reference; is valid only if compared to be equal to an association of the target side',
|
|
528
|
+
subQuery: 'Unexpected $(ID) reference in a sub query',
|
|
529
|
+
setQuery: 'Unexpected $(ID) reference in a query on the right side of $(OP)',
|
|
530
|
+
},
|
|
515
531
|
'ref-undefined-def': {
|
|
516
532
|
std: 'Artifact $(ART) has not been found',
|
|
517
533
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -550,6 +566,9 @@ const centralMessageTexts = {
|
|
|
550
566
|
std: '$(ART) can\'t be extended because it originates from an include',
|
|
551
567
|
elements: '$(ART) can\'t be extended by elements/enums because it originates from an include',
|
|
552
568
|
},
|
|
569
|
+
'ref-unexpected-many-navigation': {
|
|
570
|
+
std: 'Unexpected navigation into arrayed structure',
|
|
571
|
+
},
|
|
553
572
|
'ref-unexpected-scope': {
|
|
554
573
|
std: 'Unexpected parameter reference',
|
|
555
574
|
calc: 'Calculated elements can\'t use parameter references',
|
|
@@ -565,7 +584,11 @@ const centralMessageTexts = {
|
|
|
565
584
|
},
|
|
566
585
|
'ref-unexpected-assoc': {
|
|
567
586
|
std: 'Unexpected reference to association $(NAME)', // "std" currently unused
|
|
587
|
+
unmanaged: 'Unexpected reference to an unmanaged association',
|
|
588
|
+
'self-unmanaged': 'Unexpected column reference starting with $(ALIAS) to an unmanaged association',
|
|
589
|
+
self: 'A reference to an unmanaged association is only valid when compared via $(CODE)',
|
|
568
590
|
expr: 'Associations can\'t be used as values in expressions',
|
|
591
|
+
'expr-comp': 'Compositions can\'t be used as values in expressions',
|
|
569
592
|
cast: 'Casting to an association is not supported',
|
|
570
593
|
},
|
|
571
594
|
'ref-unexpected-calculated': {
|
|
@@ -575,7 +598,7 @@ const centralMessageTexts = {
|
|
|
575
598
|
},
|
|
576
599
|
'ref-unexpected-localized': {
|
|
577
600
|
std: 'Unexpected reference to localized element $(NAME)', // "std" currently unused
|
|
578
|
-
calc: 'Calculated elements can\'t refer to localized elements',
|
|
601
|
+
calc: 'Calculated elements "on-write" can\'t refer to localized elements',
|
|
579
602
|
},
|
|
580
603
|
|
|
581
604
|
'ref-unexpected-navigation': {
|
|
@@ -624,6 +647,14 @@ const centralMessageTexts = {
|
|
|
624
647
|
comp: 'Unexpected composition inside $(PROP)',
|
|
625
648
|
},
|
|
626
649
|
|
|
650
|
+
'type-unexpected-default': {
|
|
651
|
+
std: 'Unexpected $(KEYWORD) on an association/composition', // unused
|
|
652
|
+
multi: 'Unexpected $(KEYWORD); expected exactly one foreign key in combination with default value, but found $(COUNT)',
|
|
653
|
+
structured: 'Unexpected $(KEYWORD) in combination with structured foreign key $(NAME); $(KEYWORD) requires a non-structured foreign key',
|
|
654
|
+
'onCond': 'Unexpected $(KEYWORD) on an association/composition with ON-condition; $(KEYWORD) requires exactly one foreign key',
|
|
655
|
+
'targetAspect': 'Unexpected $(KEYWORD) on composition of aspect'
|
|
656
|
+
},
|
|
657
|
+
|
|
627
658
|
'anno-builtin': 'Builtin types should not be annotated. Use custom type instead',
|
|
628
659
|
'anno-undefined-def': 'Artifact $(ART) has not been found', // TODO: ext-
|
|
629
660
|
'anno-undefined-art': 'No artifact has been found with name $(ART)',
|
|
@@ -654,14 +685,25 @@ const centralMessageTexts = {
|
|
|
654
685
|
source: 'Unexpected definition of an association in an entity with parameters',
|
|
655
686
|
target: 'Expected association target to have no parameters',
|
|
656
687
|
},
|
|
688
|
+
'def-unexpected-nested-proj': {
|
|
689
|
+
std: 'Unexpected $(CODE)',
|
|
690
|
+
var: 'Unexpected $(CODE) after reference to CDS variable',
|
|
691
|
+
struct: 'Unexpected $(CODE); can only be used after a reference to a structure or association',
|
|
692
|
+
init: 'Unexpected $(CODE); can only be used after a reference to a structure, association or table alias',
|
|
693
|
+
},
|
|
657
694
|
'def-unexpected-calcview-assoc': {
|
|
658
695
|
std: 'unused',
|
|
659
696
|
'source': 'Unexpected definition of an association in an entity annotated with $(ANNO)',
|
|
660
697
|
'target': 'Expected association target not to be annotated with $(ANNO)',
|
|
661
698
|
},
|
|
699
|
+
'def-invalid-key': {
|
|
700
|
+
std: 'The current element can\'t be defined as primary key', // (unused)
|
|
701
|
+
unmanaged: 'Unmanaged associations/compositions can\'t be defined as primary key',
|
|
702
|
+
composition: 'Managed aspect compositions can\'t be defined as primary key',
|
|
703
|
+
},
|
|
662
704
|
'def-unexpected-key': {
|
|
663
705
|
std: '$(ART) can\'t have additional keys',
|
|
664
|
-
virtual: 'Unexpected $(PROP) for virtual element $(
|
|
706
|
+
virtual: 'Unexpected $(PROP) for virtual element $(ART)',
|
|
665
707
|
// TODO: Better message?
|
|
666
708
|
include: '$(ART) can\'t have additional keys (through include)',
|
|
667
709
|
},
|
|
@@ -718,6 +760,12 @@ const centralMessageTexts = {
|
|
|
718
760
|
elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
719
761
|
actions: 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
720
762
|
},
|
|
763
|
+
'ref-invalid-element': {
|
|
764
|
+
std: 'Invalid element reference',
|
|
765
|
+
$tableAlias: 'Can\'t refer to source elements of table alias $(ID)',
|
|
766
|
+
mixin: 'Can\'t refer to the query\'s own mixin $(ID)',
|
|
767
|
+
$self: 'Can\'t refer to the query\'s own elements',
|
|
768
|
+
},
|
|
721
769
|
'ref-invalid-override': {
|
|
722
770
|
std: 'Overridden element of include must not change element structure', // unused
|
|
723
771
|
'new-not-structured': 'Expected element $(NAME) to be structured, because it overrides the included element from $(ART)',
|
|
@@ -725,19 +773,27 @@ const centralMessageTexts = {
|
|
|
725
773
|
missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
|
|
726
774
|
},
|
|
727
775
|
|
|
728
|
-
'ref-expecting-
|
|
729
|
-
'ref-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
776
|
+
'ref-expecting-const': 'A constant expression or variable is expected here',
|
|
777
|
+
'ref-invalid-target': {
|
|
778
|
+
std: 'An entity, projection or view is expected here', // TODO: change text
|
|
779
|
+
composition: 'Expecting an entity or aspect as composition target',
|
|
780
|
+
bare: 'Expecting the target aspect to have elements',
|
|
781
|
+
aspect: 'Expecting the name of an aspect in property $(PROP)', // only CSN input
|
|
782
|
+
},
|
|
783
|
+
'ref-invalid-include': {
|
|
784
|
+
std: 'A type, entity, aspect or event with direct elements is expected here',
|
|
785
|
+
bare : 'An aspect without elements is expected here',
|
|
786
|
+
},
|
|
787
|
+
'ref-invalid-type': {
|
|
788
|
+
std: 'A type or an element is expected here',
|
|
789
|
+
param: 'A type, an element, or a service entity is expected here',
|
|
790
|
+
event: 'A type, an element, an event, or a service entity is expected here',
|
|
791
|
+
},
|
|
734
792
|
// TODO: text variant if the association does not start an entity
|
|
735
|
-
'ref-
|
|
736
|
-
'ref-expecting-target': 'An entity or an aspect is expected here', // TODO: coverage
|
|
793
|
+
'ref-invalid-source': 'A query source must be an entity or an association',
|
|
737
794
|
'extend-columns': 'Artifact $(ART) can\'t be extended with columns, only projections can',
|
|
738
795
|
'extend-repeated-intralayer': 'Unstable element order due to repeated extensions in same layer',
|
|
739
796
|
'extend-unexpected-include': 'Can\'t extend $(META) with includes',
|
|
740
|
-
'ref-expecting-bare-aspect': 'An aspect without elements is expected here',
|
|
741
797
|
|
|
742
798
|
'ext-duplicate-same-file': 'Duplicate extension with $(PROP) in same file',
|
|
743
799
|
'ext-duplicate-extend-type': 'Duplicate type extension for type $(TYPE)',
|
|
@@ -787,6 +843,8 @@ const centralMessageTexts = {
|
|
|
787
843
|
target: 'Expected target $(TARGET) of specified element $(NAME) to be the same as the inferred element\'s target $(ART)',
|
|
788
844
|
foreignKeys: 'Expected foreign keys of specified element $(NAME) to be the same as the inferred element\'s foreign keys',
|
|
789
845
|
prop: 'Value for $(PROP) of the specified element $(NAME) does not match the inferred element\'s value',
|
|
846
|
+
enumExtra: 'Specified element $(NAME) differs from inferred element: it has an additional enum element $(ID)',
|
|
847
|
+
enumVal: 'Specified element $(NAME) differs from inferred element: it has a different value for enum element $(ID)',
|
|
790
848
|
},
|
|
791
849
|
|
|
792
850
|
'query-unexpected-property': {
|
|
@@ -794,10 +852,7 @@ const centralMessageTexts = {
|
|
|
794
852
|
calculatedElement: 'Unexpected property $(PROP) in the specified element $(NAME); calculated elements are not supported in queries',
|
|
795
853
|
},
|
|
796
854
|
|
|
797
|
-
'ref-sloppy-type': 'A type or an element is expected here',
|
|
798
|
-
'ref-sloppy-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
799
855
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
800
|
-
'ref-sloppy-event-type': 'A type, an element, an event, or a service entity is expected here',
|
|
801
856
|
|
|
802
857
|
'ref-ambiguous': {
|
|
803
858
|
std: 'Replace ambiguous $(ID) by $(NAMES)',
|
|
@@ -812,16 +867,41 @@ const centralMessageTexts = {
|
|
|
812
867
|
entity: 'Entity $(ART) with managed compositions can\'t be used in types', // yet
|
|
813
868
|
},
|
|
814
869
|
|
|
870
|
+
'type-unsupported-key-change': {
|
|
871
|
+
std: 'Added element $(ID) is a primary key change and will not work if the table contains data',
|
|
872
|
+
changed: 'Changed element $(ID) is a primary key change and will not work if the table contains data'
|
|
873
|
+
},
|
|
874
|
+
|
|
875
|
+
'type-unsupported-key-sqlite': {
|
|
876
|
+
std: 'Added element $(ID) is a primary key change and will not work with dialect $(NAME)',
|
|
877
|
+
changed: 'Changed element $(ID) is a primary key change and will not work with dialect $(NAME)'
|
|
878
|
+
},
|
|
879
|
+
|
|
880
|
+
'type-invalid-cast': {
|
|
881
|
+
std: 'Invalid cast to $(TYPE)', // unused
|
|
882
|
+
'to-structure': 'Can\'t cast to a structured type',
|
|
883
|
+
'from-structure': 'Structured elements can\'t be cast to a different type',
|
|
884
|
+
'expr-to-structure': 'Can\'t cast an expression to a structured type',
|
|
885
|
+
'val-to-structure': 'Can\'t cast $(VALUE) to a structured type'
|
|
886
|
+
},
|
|
887
|
+
|
|
815
888
|
// -----------------------------------------------------------------------------------
|
|
816
889
|
// Expressions
|
|
817
890
|
// -----------------------------------------------------------------------------------
|
|
818
|
-
'
|
|
819
|
-
|
|
820
|
-
|
|
891
|
+
'type-invalid-cardinality': {
|
|
892
|
+
std: 'Invalid value $(VALUE) for cardinality', // unused variant
|
|
893
|
+
sourceMax: 'Invalid value $(PROP) for maximum source cardinality, expecting a positive number or $(OTHERPROP)',
|
|
894
|
+
targetMax: 'Invalid value $(PROP) for maximum target cardinality, expecting a positive number or $(OTHERPROP)',
|
|
895
|
+
targetMin: 'Invalid value $(PROP) for minimum target cardinality, expecting a non-negative number',
|
|
896
|
+
sourceMin: 'Invalid value $(PROP) for minimum source cardinality, expecting a non-negative number',
|
|
897
|
+
sourceVal: 'Source minimum cardinality must not be greater than source maximum cardinality',
|
|
898
|
+
targetVal: 'Target minimum cardinality must not be greater than target maximum cardinality',
|
|
821
899
|
},
|
|
822
900
|
|
|
823
901
|
'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
|
|
824
902
|
|
|
903
|
+
'expr-missing-foreign-key': 'Path step $(ID) of $(ELEMREF) has no valid foreign keys',
|
|
904
|
+
|
|
825
905
|
// OData version dependent messages
|
|
826
906
|
'odata-spec-violation-array': 'Unexpected array type for OData $(VERSION)',
|
|
827
907
|
'odata-spec-violation-param' : 'Expected parameter to be typed with either scalar or structured type for OData $(VERSION)',
|
|
@@ -854,6 +934,7 @@ const centralMessageTexts = {
|
|
|
854
934
|
incompatible: 'Unexpected EDM Type $(TYPE) for OData $(VERSION)',
|
|
855
935
|
facet: 'Unexpected EDM Type facet $(NAME) of type $(TYPE) for OData $(VERSION)',
|
|
856
936
|
external: 'Referenced type $(TYPE) marked as $(ANNO) can\'t be rendered as $(CODE) in service $(NAME) for OData $(VERSION)',
|
|
937
|
+
scale: 'Expected scale $(NUMBER) to be less than or equal to precision $(RAWVALUE)'
|
|
857
938
|
},
|
|
858
939
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(META)',
|
|
859
940
|
'odata-spec-violation-namespace': {
|
|
@@ -919,7 +1000,7 @@ const centralMessageTexts = {
|
|
|
919
1000
|
// -----------------------------------------------------------------------------------
|
|
920
1001
|
'enum': 'Value $(VALUE) is not one out of $(RAWVALUES) for $(ANNO) of type $(TYPE)',
|
|
921
1002
|
'std': 'Unexpected value $(VALUE) for $(ANNO) of type $(TYPE)',
|
|
922
|
-
'
|
|
1003
|
+
'incompval': 'Unexpected $(STR) value for $(ANNO) of type $(TYPE)',
|
|
923
1004
|
'nestedcollection': 'Nested collections are not supported for $(ANNO)',
|
|
924
1005
|
'enumincollection': 'Enum inside collection is not supported for $(ANNO)',
|
|
925
1006
|
'multexpr': 'EDM JSON code contains more than one dynamic expression: $(RAWVALUES) for $(ANNO)',
|
|
@@ -948,7 +1029,7 @@ const centralMessageTexts = {
|
|
|
948
1029
|
*
|
|
949
1030
|
* @typedef {object} MessageConfig
|
|
950
1031
|
* @property {MessageSeverity} severity Default severity for the message.
|
|
951
|
-
* @property {string[]|'deprecated'|'v4'|true} [configurableFor]
|
|
1032
|
+
* @property {string[]|'deprecated'|'v4'|'test'|true} [configurableFor]
|
|
952
1033
|
* Whether the error can be reclassified to a warning or lower.
|
|
953
1034
|
* If not `true` then an array is expected with specified modules in which the error is downgradable.
|
|
954
1035
|
* Only has an effect if default severity is 'Error'.
|
package/lib/base/messages.js
CHANGED
|
@@ -17,6 +17,7 @@ const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { inspect } = require('util')
|
|
20
|
+
const { CsnLocation } = require('../compiler/classes');
|
|
20
21
|
|
|
21
22
|
// term instance for messages
|
|
22
23
|
const colorTerm = term();
|
|
@@ -94,7 +95,7 @@ class CompilationError extends Error {
|
|
|
94
95
|
super( `CDS compilation failed\n${messages?.map( m => m.toString() ).join('\n') || ''}` );
|
|
95
96
|
/** @since v4.0.0 */
|
|
96
97
|
this.code = 'ERR_CDS_COMPILATION_FAILURE';
|
|
97
|
-
this.messages = messages;
|
|
98
|
+
this.messages = [ ...messages ].sort(compareMessageSeverityAware);
|
|
98
99
|
|
|
99
100
|
// TODO: remove this bin/cdsc.js specifics
|
|
100
101
|
Object.defineProperty( this, 'hasBeenReported', { value: false, configurable: true, writable: true, enumerable: false } );
|
|
@@ -128,7 +129,7 @@ class CompilationError extends Error {
|
|
|
128
129
|
class CompileMessage {
|
|
129
130
|
/**
|
|
130
131
|
* Creates an instance of CompileMessage.
|
|
131
|
-
* @param {
|
|
132
|
+
* @param {CSN.Location} location Location of the message
|
|
132
133
|
* @param {string} msg The message text
|
|
133
134
|
* @param {MessageSeverity} [severity='Error'] Severity: Debug, Info, Warning, Error
|
|
134
135
|
* @param {string} [id] The ID of the message - visible as property messageId
|
|
@@ -139,7 +140,7 @@ class CompileMessage {
|
|
|
139
140
|
*/
|
|
140
141
|
constructor(location, msg, severity = 'Error', id = null, home = null, moduleName = null) {
|
|
141
142
|
this.message = msg;
|
|
142
|
-
this.$location = { ...location, address: undefined };
|
|
143
|
+
this.$location = { __proto__: CsnLocation.prototype, ...location, address: undefined };
|
|
143
144
|
this.validNames = null;
|
|
144
145
|
this.home = home; // semantic location, e.g. 'entity:"E"/element:"x"'
|
|
145
146
|
this.severity = severity;
|
|
@@ -892,14 +893,14 @@ function replaceInString( text, params ) {
|
|
|
892
893
|
}
|
|
893
894
|
|
|
894
895
|
/**
|
|
895
|
-
* @param {
|
|
896
|
-
* @returns {
|
|
896
|
+
* @param {CsnLocation} loc
|
|
897
|
+
* @returns {CsnLocation}
|
|
897
898
|
*/
|
|
898
899
|
function weakLocation( loc ) {
|
|
899
900
|
if (!loc)
|
|
900
|
-
return
|
|
901
|
+
return new CsnLocation();
|
|
901
902
|
// no endLine/endCol
|
|
902
|
-
return
|
|
903
|
+
return new CsnLocation( loc.file, loc.line, loc.col, undefined, undefined );
|
|
903
904
|
}
|
|
904
905
|
|
|
905
906
|
/**
|
|
@@ -1299,6 +1300,13 @@ function deduplicateMessages( messages ) {
|
|
|
1299
1300
|
|
|
1300
1301
|
function shortArtName( art ) {
|
|
1301
1302
|
const name = getArtifactName( art );
|
|
1303
|
+
if (!name) {
|
|
1304
|
+
const loc = art.location ? ` at ${ locationString( art.location ) }` : '';
|
|
1305
|
+
throw new CompilerAssertion(
|
|
1306
|
+
art.path
|
|
1307
|
+
? `No artifact for ${ art.path.map( i => i.id ).join( '.' )}${ loc }`
|
|
1308
|
+
: `No name found in ${ Object.keys( art ).join( '+' )}${ loc }` );
|
|
1309
|
+
}
|
|
1302
1310
|
if ([ 'select', 'action', 'alias', 'param' ].every( n => name[n] == null || name[n] === 1 ) &&
|
|
1303
1311
|
!name.absolute.includes(':'))
|
|
1304
1312
|
return quote.double( name.element ? `${ name.absolute }:${ name.element }` : name.absolute );
|
|
@@ -1310,14 +1318,14 @@ function artName( art, omit ) {
|
|
|
1310
1318
|
const r = (name.absolute) ? [ quoted( name.absolute ) ] : [];
|
|
1311
1319
|
if (name.select && name.select > 1 || name.select != null && art.kind !== 'element') // Yes, omit select:1 for element - TODO: re-check
|
|
1312
1320
|
r.push( (art.kind === 'extend' ? 'block:' : 'query:') + name.select ); // TODO: rename to 'select:1' and consider whether there are more selects
|
|
1313
|
-
if (name.action && omit !== 'action')
|
|
1321
|
+
if (name.action != null && omit !== 'action')
|
|
1314
1322
|
r.push( `${ memberActionName(art) }:${ quoted( name.action ) }` );
|
|
1315
|
-
if (name.alias && art.kind !== '$self' && name.$inferred !== '$internal')
|
|
1323
|
+
if (name.alias != null && art.kind !== '$self' && name.$inferred !== '$internal')
|
|
1316
1324
|
r.push( (art.kind === 'mixin' ? 'mixin:' : 'alias:') + quoted( name.alias ) );
|
|
1317
1325
|
if (name.param != null && omit !== 'param')
|
|
1318
1326
|
r.push( name.param ? `param:${ quoted( name.param ) }` : 'returns' ); // TODO: join
|
|
1319
1327
|
|
|
1320
|
-
if (name.element && omit !== 'element') {
|
|
1328
|
+
if (name.element != null && omit !== 'element') {
|
|
1321
1329
|
if (name.select != null && !art.$inferred)
|
|
1322
1330
|
r.push( 'column:' + quoted( name.element ) );
|
|
1323
1331
|
else
|
package/lib/base/model.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// module- and csn/XSN-independent definitions
|
|
2
|
+
|
|
3
|
+
// TODO: move XSN-specific things to lib/compiler/utils/
|
|
4
|
+
// TODO: move CSN-specific things to ???
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
const { forEach } = require('../utils/objectUtils');
|
|
@@ -22,14 +27,14 @@ const queryOps = {
|
|
|
22
27
|
const availableBetaFlags = {
|
|
23
28
|
// enabled by --beta-mode
|
|
24
29
|
annotationExpressions: true,
|
|
25
|
-
toRename: true, // Removes once it's publicly documented
|
|
26
30
|
assocsWithParams: true, // beta, because runtimes don't support it, yet.
|
|
27
31
|
hanaAssocRealCardinality: true,
|
|
28
32
|
mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
|
|
29
33
|
enableUniversalCsn: true,
|
|
30
|
-
aspectWithoutElements: true, // TODO: do not just remove beta flag - remove elements, too!
|
|
31
34
|
odataTerms: true,
|
|
32
35
|
optionalActionFunctionParameters: true, // not supported by runtime, yet.
|
|
36
|
+
associationDefault: true,
|
|
37
|
+
annotateForeignKeys: true,
|
|
33
38
|
// disabled by --beta-mode
|
|
34
39
|
nestedServices: false,
|
|
35
40
|
};
|
|
@@ -40,6 +45,7 @@ const availableDeprecatedFlags = {
|
|
|
40
45
|
downgradableErrors: true,
|
|
41
46
|
includesNonShadowedFirst: true,
|
|
42
47
|
eagerPersistenceForGeneratedEntities: true,
|
|
48
|
+
noKeyPropagationWithExpansions: true,
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
const oldDeprecatedFlags_v2 = [
|
|
@@ -126,18 +132,26 @@ function checkRemovedDeprecatedFlags( options, { error } ) {
|
|
|
126
132
|
});
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Apply function `callback` to all artifacts in dictionary
|
|
137
|
+
* `model.definitions`. See function `forEachGeneric` for details.
|
|
138
|
+
* TODO: should we skip "namespaces" already here?
|
|
139
|
+
*/
|
|
132
140
|
function forEachDefinition( model, callback ) {
|
|
133
141
|
forEachGeneric( model, 'definitions', callback );
|
|
134
142
|
}
|
|
135
143
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Apply function `callback` to all members of object `obj` (main artifact or
|
|
146
|
+
* parent member). Members are considered those in dictionaries `elements`,
|
|
147
|
+
* `enum`, `actions` and `params` of `obj`, `elements` and `enums` are also
|
|
148
|
+
* searched inside property `items` (array of). See function `forEachGeneric`
|
|
149
|
+
* for details.
|
|
150
|
+
*
|
|
151
|
+
* @param {XSN.Artifact} construct
|
|
152
|
+
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
153
|
+
* @param {XSN.Artifact} [target]
|
|
154
|
+
*/
|
|
141
155
|
function forEachMember( construct, callback, target ) {
|
|
142
156
|
let obj = construct;
|
|
143
157
|
while (obj.items)
|
|
@@ -151,6 +165,24 @@ function forEachMember( construct, callback, target ) {
|
|
|
151
165
|
callback( construct.returns, '', 'params' );
|
|
152
166
|
}
|
|
153
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Same as forEachMember, but inside each member, calls itself recursively, i.e.
|
|
170
|
+
* sub members are traversed as well.
|
|
171
|
+
*
|
|
172
|
+
* @param {XSN.Artifact} construct
|
|
173
|
+
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
174
|
+
* @param {XSN.Artifact} [target]
|
|
175
|
+
*/
|
|
176
|
+
function forEachMemberRecursively( construct, callback, target ) {
|
|
177
|
+
forEachMember( construct, ( member, memberName, prop ) => {
|
|
178
|
+
callback( member, memberName, prop );
|
|
179
|
+
if (Array.isArray(member.$duplicates)) // redefinitions
|
|
180
|
+
member.$duplicates.forEach( o => callback( o, memberName, prop ) );
|
|
181
|
+
// Descend into nested members, too
|
|
182
|
+
forEachMemberRecursively( member, callback );
|
|
183
|
+
}, target);
|
|
184
|
+
}
|
|
185
|
+
|
|
154
186
|
// Apply function `callback` to all objects in dictionary `dict`, including all
|
|
155
187
|
// duplicates (found under the same name). Function `callback` is called with
|
|
156
188
|
// the following arguments: the object, the name, and -if it is a duplicate-
|
|
@@ -193,6 +225,7 @@ module.exports = {
|
|
|
193
225
|
queryOps,
|
|
194
226
|
forEachDefinition,
|
|
195
227
|
forEachMember,
|
|
228
|
+
forEachMemberRecursively,
|
|
196
229
|
forEachGeneric,
|
|
197
230
|
forEachInOrder,
|
|
198
231
|
setProp,
|
|
@@ -53,12 +53,12 @@ function rejectParamDefaultsInHanaCds( member, memberName, prop, path ) {
|
|
|
53
53
|
*/
|
|
54
54
|
function warnAboutDefaultOnAssociationForHanaCds( member, memberName, prop, path ) {
|
|
55
55
|
const art = this.csn.definitions[path[1]];
|
|
56
|
-
if (!art.query && !art.projection &&
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
if (this.options.transformation === 'hdbcds' && !art.query && !art.projection && member.target && member.default) {
|
|
57
|
+
const type = member._type?.type || member.type || 'cds.Association';
|
|
58
|
+
this.warning('type-invalid-default', path, { '#': type === 'cds.Association' ? 'std' : 'comp' }, {
|
|
59
|
+
std: 'Default on associations is not supported for HDBCDS',
|
|
60
|
+
comp: 'Default on compositions is not supported for HDBCDS',
|
|
61
|
+
});
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
package/lib/checks/elements.js
CHANGED
|
@@ -119,18 +119,19 @@ function checkManagedAssoc( art ) {
|
|
|
119
119
|
forEachMemberRecursively(art, (member) => {
|
|
120
120
|
if (this.csnUtils.isAssocOrComposition(member) &&
|
|
121
121
|
!isManagedComposition.bind(this)(member)) {
|
|
122
|
+
// Implementation note: Imported services (i.e. external ones) may contain to-many associations
|
|
123
|
+
// with an empty foreign key list. If the user (in this case importer) explicitly sets an empty
|
|
124
|
+
// foreign key array, we won't emit a warning to avoid spamming the user.
|
|
122
125
|
const max = member.cardinality?.max ? member.cardinality.max : 1;
|
|
123
|
-
if (max !== 1 && !member.on) {
|
|
126
|
+
if (max !== 1 && !member.on && (!member.keys || member.keys.length > 0)) {
|
|
124
127
|
const isNoDb = art['@cds.persistence.skip'] || art['@cds.persistence.exists'];
|
|
125
|
-
this.warning(isNoDb ? 'to-many-no-on-noDB' : 'to-many-no-on', member.cardinality ? member.cardinality.$path : member.$path,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
comp: 'Expected composition with target cardinality $(VALUE) to have an ON-condition',
|
|
133
|
-
});
|
|
128
|
+
this.warning(isNoDb ? 'to-many-no-on-noDB' : 'to-many-no-on', member.cardinality ? member.cardinality.$path : member.$path, {
|
|
129
|
+
value: cardinality2str(member, false),
|
|
130
|
+
'#': this.csnUtils.isComposition(member) ? 'comp' : 'std',
|
|
131
|
+
}, {
|
|
132
|
+
std: 'Expected association with target cardinality $(VALUE) to have an ON-condition',
|
|
133
|
+
comp: 'Expected composition with target cardinality $(VALUE) to have an ON-condition',
|
|
134
|
+
});
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
});
|
|
@@ -16,8 +16,6 @@ const { setProp } = require('../base/model');
|
|
|
16
16
|
function validateForeignKeys( member, memberName ) {
|
|
17
17
|
// We have a managed association
|
|
18
18
|
const isManagedAssoc = mem => mem && mem.target && !mem.on;
|
|
19
|
-
// We have an unmanaged association
|
|
20
|
-
const isUnmanagedAssoc = mem => mem && mem.target && mem.on && !mem.keys;
|
|
21
19
|
|
|
22
20
|
// Declared as arrow-function to keep scope the same (this value)
|
|
23
21
|
const handleAssociation = (mem) => {
|
|
@@ -46,9 +44,6 @@ function validateForeignKeys( member, memberName ) {
|
|
|
46
44
|
if (mem.items) {
|
|
47
45
|
this.error(null, member.$path, {}, 'Array-like properties must not be foreign keys');
|
|
48
46
|
}
|
|
49
|
-
else if (isUnmanagedAssoc(mem)) {
|
|
50
|
-
this.error(null, member.$path, {}, 'Unmanaged association must not be a foreign key');
|
|
51
|
-
}
|
|
52
47
|
else if (mem.keys) {
|
|
53
48
|
handleAssociation(mem);
|
|
54
49
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { applyTransformationsOnNonDictionary } = require('../model/csnUtils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check all refs in the given parent for the traversal of paths
|
|
7
|
+
* into `.items`
|
|
8
|
+
*
|
|
9
|
+
* @param {object} parent Object with the expression as a property
|
|
10
|
+
* @param {string} propOnParent Name of the expression property on parent
|
|
11
|
+
* @param {Array} e Expression to check - see module.exports
|
|
12
|
+
* @param {CSN.Path} path
|
|
13
|
+
*/
|
|
14
|
+
function navigationIntoMany( parent, propOnParent, e, path ) {
|
|
15
|
+
applyTransformationsOnNonDictionary(parent, propOnParent, {
|
|
16
|
+
ref: (_parent, _prop, ref, _path) => {
|
|
17
|
+
const itemNavigationIndex = _parent._links?.findIndex(l => l.art.items);
|
|
18
|
+
if (itemNavigationIndex !== -1 && _parent.ref.length > itemNavigationIndex + 1)
|
|
19
|
+
this.message('ref-unexpected-many-navigation', _path);
|
|
20
|
+
},
|
|
21
|
+
}, { skipStandard: { type: true } }, path);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
columns: navigationIntoMany,
|
|
26
|
+
from: navigationIntoMany,
|
|
27
|
+
on: navigationIntoMany,
|
|
28
|
+
having: navigationIntoMany,
|
|
29
|
+
groupBy: navigationIntoMany,
|
|
30
|
+
orderBy: navigationIntoMany,
|
|
31
|
+
where: navigationIntoMany,
|
|
32
|
+
xpr: navigationIntoMany,
|
|
33
|
+
};
|