@sap/cds-compiler 3.9.8 → 3.9.12
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 +25 -0
- package/lib/api/main.js +15 -15
- package/lib/api/options.js +3 -3
- package/lib/base/message-registry.js +13 -4
- package/lib/checks/validator.js +4 -4
- package/lib/compiler/builtins.js +2 -2
- package/lib/compiler/resolve.js +4 -8
- package/lib/edm/annotations/genericTranslation.js +56 -54
- package/lib/edm/annotations/preprocessAnnotations.js +4 -4
- package/lib/edm/csn2edm.js +11 -1
- package/lib/edm/edmUtils.js +1 -1
- package/lib/transform/db/applyTransformations.js +6 -6
- package/lib/transform/db/expansion.js +16 -7
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/transformExists.js +2 -0
- package/lib/transform/forOdataNew.js +5 -1
- package/lib/transform/localized.js +16 -7
- package/lib/transform/translateAssocsToJoins.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@
|
|
|
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
|
+
|
|
11
|
+
## Version 3.9.12 - 2023-12-06
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- compiler:
|
|
16
|
+
+ SQL function `STDDEV(*)` was not parsable.
|
|
17
|
+
+ Numbers in scientific notation `-1e1` were sometimes not recognized via CSN input.
|
|
18
|
+
- for.odata: Fix crash when using a projection with associations as action parameter type.
|
|
19
|
+
- for.hana: Fix a bug in association to join translation, expect ON condition operand to be a function without arguments.
|
|
20
|
+
- to.edm(x):
|
|
21
|
+
+ Omit `EntitySet` attribute on `Edm.FunctionImport` and `Edm.ActionImport` that return a singleton.
|
|
22
|
+
+ Don't render `Scale: variable` for `cds.Decimal(scale:0)`.
|
|
23
|
+
- to.sql/hdi/hdbcds: consider `having` predicate for `exists` expansion
|
|
24
|
+
|
|
25
|
+
## Version 3.9.10 - 2023-08-25
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- to.edm(x): Error reporting for incorrect handling of "Collection()" has been improved.
|
|
30
|
+
- to.sql/hdi/hdbcds: Views on views with parameters did not have localized convenience views based on
|
|
31
|
+
other localized views (missing `localized.` prefix in FROM clause)
|
|
32
|
+
- to.sql: Casting expressions to a structured type yields a proper error instead of strange compiler error.
|
|
33
|
+
- to.sql.migration: Don't drop-create views marked with `@cds.persistence.exists` or `@cds.persistence.skip`
|
|
34
|
+
|
|
10
35
|
## Version 3.9.8 - 2023-08-03
|
|
11
36
|
|
|
12
37
|
### Fixed
|
package/lib/api/main.js
CHANGED
|
@@ -48,7 +48,7 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
|
|
|
48
48
|
* @param {string} transformation Name of the transformation - odata or hana
|
|
49
49
|
* @param {NestedOptions} options Options used for the transformation
|
|
50
50
|
* @param {string[]} relevantOptionNames Option names that are defining characteristics
|
|
51
|
-
* @param {string[]} [optionalOptionNames
|
|
51
|
+
* @param {string[]} [optionalOptionNames] Option names that should be attached as a fyi
|
|
52
52
|
*/
|
|
53
53
|
function attachTransformerCharacteristics( csn, transformation, options,
|
|
54
54
|
relevantOptionNames, optionalOptionNames = [] ) {
|
|
@@ -141,7 +141,7 @@ function odataInternal( csn, internalOptions ) {
|
|
|
141
141
|
* Return a odata-transformed CSN
|
|
142
142
|
*
|
|
143
143
|
* @param {CSN.Model} csn Clean input CSN
|
|
144
|
-
* @param {ODataOptions} [options
|
|
144
|
+
* @param {ODataOptions} [options] Options
|
|
145
145
|
* @returns {oDataCSN} Return an oData-pre-processed CSN
|
|
146
146
|
*/
|
|
147
147
|
function odata( csn, options = {} ) {
|
|
@@ -154,7 +154,7 @@ function odata( csn, options = {} ) {
|
|
|
154
154
|
* Process the given csn back to cdl.
|
|
155
155
|
*
|
|
156
156
|
* @param {object} csn CSN to process
|
|
157
|
-
* @param {object} [options
|
|
157
|
+
* @param {object} [options] Options
|
|
158
158
|
* @returns {object} { model: string, namespace: string }
|
|
159
159
|
*/
|
|
160
160
|
function cdl( csn, options = {} ) {
|
|
@@ -167,7 +167,7 @@ function cdl( csn, options = {} ) {
|
|
|
167
167
|
* Transform a CSN like to.sql
|
|
168
168
|
*
|
|
169
169
|
* @param {CSN.Model} csn Plain input CSN
|
|
170
|
-
* @param {SqlOptions} [options
|
|
170
|
+
* @param {SqlOptions} [options] Options
|
|
171
171
|
* @returns {CSN.Model} CSN transformed like to.sql
|
|
172
172
|
* @private
|
|
173
173
|
*/
|
|
@@ -182,7 +182,7 @@ function forSql( csn, options = {} ) {
|
|
|
182
182
|
* Transform a CSN like to.hdi
|
|
183
183
|
*
|
|
184
184
|
* @param {CSN.Model} csn Plain input CSN
|
|
185
|
-
* @param {HdiOptions} [options
|
|
185
|
+
* @param {HdiOptions} [options] Options
|
|
186
186
|
* @returns {CSN.Model} CSN transformed like to.hdi
|
|
187
187
|
* @private
|
|
188
188
|
*/
|
|
@@ -197,7 +197,7 @@ function forHdi( csn, options = {} ) {
|
|
|
197
197
|
* Transform a CSN like to.hdbcds
|
|
198
198
|
*
|
|
199
199
|
* @param {CSN.Model} csn Plain input CSN
|
|
200
|
-
* @param {HdbcdsOptions} [options
|
|
200
|
+
* @param {HdbcdsOptions} [options] Options
|
|
201
201
|
* @returns {CSN.Model} CSN transformed like to.hdbcds
|
|
202
202
|
* @private
|
|
203
203
|
*/
|
|
@@ -214,7 +214,7 @@ function forHdbcds( csn, options = {} ) {
|
|
|
214
214
|
* Process the given CSN into SQL.
|
|
215
215
|
*
|
|
216
216
|
* @param {CSN.Model} csn A clean input CSN
|
|
217
|
-
* @param {SqlOptions} [options
|
|
217
|
+
* @param {SqlOptions} [options] Options
|
|
218
218
|
* @returns {SQL[]} Array of SQL statements, tables first, views second
|
|
219
219
|
*/
|
|
220
220
|
function sql( csn, options = {} ) {
|
|
@@ -235,7 +235,7 @@ function sql( csn, options = {} ) {
|
|
|
235
235
|
* Process the given CSN into HDI artifacts.
|
|
236
236
|
*
|
|
237
237
|
* @param {CSN.Model} csn A clean input CSN
|
|
238
|
-
* @param {HdiOptions} [options
|
|
238
|
+
* @param {HdiOptions} [options] Options
|
|
239
239
|
* @returns {HDIArtifacts} { <filename>:<content>, ...}
|
|
240
240
|
*/
|
|
241
241
|
function hdi( csn, options = {} ) {
|
|
@@ -375,8 +375,7 @@ function sqlMigration( csn, options, beforeImage ) {
|
|
|
375
375
|
const beforeArtifact = beforeImage.definitions[artifactName];
|
|
376
376
|
const diffArtifact = diff.definitions[artifactName];
|
|
377
377
|
// TODO: exists, abstract? isPersistedOnDb?
|
|
378
|
-
if (diffArtifact && diffArtifact['@cds.persistence.name'] &&
|
|
379
|
-
(diffArtifact.query || diffArtifact.projection) &&
|
|
378
|
+
if (diffArtifact && diffArtifact['@cds.persistence.name'] && csnUtils.isPersistedAsView(diffArtifact) &&
|
|
380
379
|
(diffArtifact[modelCompare.isChanged] === true || // we know it changed because we compared two views
|
|
381
380
|
diffArtifact[modelCompare.isChanged] === undefined)) { // if it was removed in the after, then we don't have the flag
|
|
382
381
|
drops.creates[artifactName] = `DROP VIEW ${ identifierUtils.renderArtifactName(artifactName) };`;
|
|
@@ -384,6 +383,7 @@ function sqlMigration( csn, options, beforeImage ) {
|
|
|
384
383
|
else if (diffArtifact &&
|
|
385
384
|
diffArtifact['@cds.persistence.skip'] !== true &&
|
|
386
385
|
diffArtifact.kind === beforeArtifact.kind && // detect action -> entity
|
|
386
|
+
csnUtils.isPersistedAsTable(diffArtifact) === csnUtils.isPersistedAsTable(beforeArtifact) && // detect removal of @cds.persistence.exists
|
|
387
387
|
csnUtils.isPersistedAsView(diffArtifact) === csnUtils.isPersistedAsView(beforeArtifact) // detect view -> entity
|
|
388
388
|
) { // don't render again, but need info for primary key extension
|
|
389
389
|
diffArtifact['@cds.persistence.skip'] = true;
|
|
@@ -529,7 +529,7 @@ sql.migration = sqlMigration;
|
|
|
529
529
|
* Process the given CSN into HDBCDS artifacts.
|
|
530
530
|
*
|
|
531
531
|
* @param {any} csn A clean input CSN
|
|
532
|
-
* @param {HdbcdsOptions} [options
|
|
532
|
+
* @param {HdbcdsOptions} [options] Options
|
|
533
533
|
* @returns {HDBCDS} { <filename>:<content>, ...}
|
|
534
534
|
*/
|
|
535
535
|
function hdbcds( csn, options = {} ) {
|
|
@@ -546,7 +546,7 @@ function hdbcds( csn, options = {} ) {
|
|
|
546
546
|
* Generate a edm document for the given service
|
|
547
547
|
*
|
|
548
548
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
549
|
-
* @param {ODataOptions} [options
|
|
549
|
+
* @param {ODataOptions} [options] Options
|
|
550
550
|
* @returns {edm} The JSON representation of the service
|
|
551
551
|
*/
|
|
552
552
|
function edm( csn, options = {} ) {
|
|
@@ -577,7 +577,7 @@ edm.all = edmall;
|
|
|
577
577
|
* Generate edm documents for all services
|
|
578
578
|
*
|
|
579
579
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
580
|
-
* @param {ODataOptions} [options
|
|
580
|
+
* @param {ODataOptions} [options] Options
|
|
581
581
|
* @returns {edms} { <service>:<JSON representation>, ...}
|
|
582
582
|
*/
|
|
583
583
|
function edmall( csn, options = {} ) {
|
|
@@ -608,7 +608,7 @@ function edmall( csn, options = {} ) {
|
|
|
608
608
|
* Generate a edmx document for the given service
|
|
609
609
|
*
|
|
610
610
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
611
|
-
* @param {ODataOptions} [options
|
|
611
|
+
* @param {ODataOptions} [options] Options
|
|
612
612
|
* @returns {edmx} The XML representation of the service
|
|
613
613
|
*/
|
|
614
614
|
function edmx( csn, options = {} ) {
|
|
@@ -640,7 +640,7 @@ edmx.all = edmxall;
|
|
|
640
640
|
* Generate edmx documents for all services
|
|
641
641
|
*
|
|
642
642
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
643
|
-
* @param {ODataOptions} [options
|
|
643
|
+
* @param {ODataOptions} [options] Options
|
|
644
644
|
* @returns {edmxs} { <service>:<XML representation>, ...}
|
|
645
645
|
*/
|
|
646
646
|
function edmxall( csn, options = {} ) {
|
package/lib/api/options.js
CHANGED
|
@@ -74,9 +74,9 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
|
|
|
74
74
|
* Apply defaults and make sure that the "hard requirements" are met,
|
|
75
75
|
* i.e. src: sql if to.sql() was called.
|
|
76
76
|
*
|
|
77
|
-
* @param {FlatOptions} [input
|
|
78
|
-
* @param {FlatOptions} [defaults
|
|
79
|
-
* @param {FlatOptions} [hardRequire
|
|
77
|
+
* @param {FlatOptions} [input] Input options
|
|
78
|
+
* @param {FlatOptions} [defaults] Default options to apply
|
|
79
|
+
* @param {FlatOptions} [hardRequire] Hard requirements to enforce
|
|
80
80
|
* @param {object} [customValidators] Custom validations to run instead of defaults
|
|
81
81
|
* @param {string[]} [combinationValidators] Option combinations to validate
|
|
82
82
|
* @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
|
|
@@ -742,6 +742,14 @@ const centralMessageTexts = {
|
|
|
742
742
|
entity: 'Entity $(ART) with managed compositions can\'t be used in types', // yet
|
|
743
743
|
},
|
|
744
744
|
|
|
745
|
+
'type-invalid-cast': {
|
|
746
|
+
std: 'Invalid cast to $(TYPE)', // unused
|
|
747
|
+
'to-structure': 'Can\'t cast to a structured type',
|
|
748
|
+
'from-structure': 'Structured elements can\'t be cast to a different type',
|
|
749
|
+
'expr-to-structure': 'Can\'t cast an expression to a structured type',
|
|
750
|
+
'val-to-structure': 'Can\'t cast $(VALUE) to a structured type'
|
|
751
|
+
},
|
|
752
|
+
|
|
745
753
|
// -----------------------------------------------------------------------------------
|
|
746
754
|
// Expressions
|
|
747
755
|
// -----------------------------------------------------------------------------------
|
|
@@ -784,6 +792,7 @@ const centralMessageTexts = {
|
|
|
784
792
|
incompatible: 'Unexpected EDM Type $(TYPE) for OData $(VERSION)',
|
|
785
793
|
facet: 'Unexpected EDM Type facet $(NAME) of type $(TYPE) for OData $(VERSION)',
|
|
786
794
|
external: 'Referenced type $(TYPE) marked as $(ANNO) can\'t be rendered as $(CODE) in service $(NAME) for OData $(VERSION)',
|
|
795
|
+
scale: 'Expected scale $(NUMBER) to be less than or equal to precision $(RAWVALUE)'
|
|
787
796
|
},
|
|
788
797
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(META)',
|
|
789
798
|
'odata-spec-violation-namespace': {
|
|
@@ -811,7 +820,7 @@ const centralMessageTexts = {
|
|
|
811
820
|
'odata-anno-preproc': {
|
|
812
821
|
'std': 'unused message text',
|
|
813
822
|
'nokey': 'Expected target $(NAME) to have a key element for $(ANNO)',
|
|
814
|
-
'multkeys': 'Expected target $(NAME) to have only one key element',
|
|
823
|
+
'multkeys': 'Expected target $(NAME) to have only one key element for $(ANNO)',
|
|
815
824
|
'vhlnokey': 'Expected value help list entity $(NAME) to have a key element for $(ANNO)',
|
|
816
825
|
'vhlmultkeys': 'Expected value help list entity $(NAME) to have only one key element for $(ANNO)',
|
|
817
826
|
'notforentity': 'Unexpected usage of $(ANNO) for an entity',
|
|
@@ -819,8 +828,8 @@ const centralMessageTexts = {
|
|
|
819
828
|
'noassoc': 'Expected association $(ID) to exist for $(ANNO)',
|
|
820
829
|
'vallistignored': '$(NAME) is ignored for $(ANNO) as $(CODE) is present',
|
|
821
830
|
'notastring': 'Expected value to be a string for $(ANNO)',
|
|
822
|
-
'notexist': '
|
|
823
|
-
'txtarr': '
|
|
831
|
+
'notexist': 'Expected entity $(ID) to exist for $(ANNO)',
|
|
832
|
+
'txtarr': 'Expected $(ANNO) shortcut to have a $(NAME) annotation'
|
|
824
833
|
},
|
|
825
834
|
// -----------------------------------------------------------------------------------
|
|
826
835
|
// GenericTranslation:
|
|
@@ -849,7 +858,7 @@ const centralMessageTexts = {
|
|
|
849
858
|
// -----------------------------------------------------------------------------------
|
|
850
859
|
'enum': 'Value $(VALUE) is not one out of $(RAWVALUES) for $(ANNO) of type $(TYPE)',
|
|
851
860
|
'std': 'Unexpected value $(VALUE) for $(ANNO) of type $(TYPE)',
|
|
852
|
-
'
|
|
861
|
+
'incompval': 'Unexpected $(STR) value for $(ANNO) of type $(TYPE)',
|
|
853
862
|
'nestedcollection': 'Nested collections are not supported for $(ANNO)',
|
|
854
863
|
'enumincollection': 'Enum inside collection is not supported for $(ANNO)',
|
|
855
864
|
'multexpr': 'EDM JSON code contains more than one dynamic expression: $(RAWVALUES) for $(ANNO)',
|
package/lib/checks/validator.js
CHANGED
|
@@ -127,10 +127,10 @@ const commonQueryValidators = [ validateMixinOnCondition ];
|
|
|
127
127
|
*
|
|
128
128
|
* @param {CSN.Model} csn CSN to check
|
|
129
129
|
* @param {object} that Will be provided to the validators via "this"
|
|
130
|
-
* @param {object[]} [csnValidators
|
|
131
|
-
* @param {Function[]} [memberValidators
|
|
132
|
-
* @param {Function[]} [artifactValidators
|
|
133
|
-
* @param {Function[]} [queryValidators
|
|
130
|
+
* @param {object[]} [csnValidators] Validations on whole CSN using applyTransformations
|
|
131
|
+
* @param {Function[]} [memberValidators] Validations on member-level
|
|
132
|
+
* @param {Function[]} [artifactValidators] Validations on artifact-level
|
|
133
|
+
* @param {Function[]} [queryValidators] Validations on query-level
|
|
134
134
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated e.g. 'action' and 'function' for hana
|
|
135
135
|
* @returns {Function} Function taking no parameters, that cleans up the attached helpers
|
|
136
136
|
*/
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -119,7 +119,7 @@ const specialFunctions = compileFunctions( {
|
|
|
119
119
|
MAX: 'COUNT',
|
|
120
120
|
SUM: 'COUNT',
|
|
121
121
|
AVG: 'COUNT',
|
|
122
|
-
|
|
122
|
+
STDDEV: 'COUNT',
|
|
123
123
|
VAR: 'COUNT',
|
|
124
124
|
LOCATE_REGEXPR: [
|
|
125
125
|
{
|
|
@@ -216,7 +216,7 @@ const timeRegEx = /^T?(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?(?:Z|[+-]\d{2}(?::\d{2})
|
|
|
216
216
|
// eslint-disable-next-line max-len
|
|
217
217
|
const timestampRegEx = /^(-?\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(\.\d{1,7})?)?(?:Z|[+-]\d{2}(?::\d{2})?)?$/;
|
|
218
218
|
// YYYY - MM - dd T HH : mm : ss . fraction TZD
|
|
219
|
-
const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]
|
|
219
|
+
const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?[ \t]*$/i;
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
222
|
* Patterns for literal token tests and creation. The value is a map from the
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -516,14 +516,10 @@ function resolve( model ) {
|
|
|
516
516
|
? art.value.args[0]?._artifact
|
|
517
517
|
: art.value._artifact;
|
|
518
518
|
if (elem && art.type) { // has explicit type
|
|
519
|
-
if (art.type._artifact?.elements)
|
|
520
|
-
error('type-cast
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
else if (elem.elements) { // TODO: calc elements
|
|
524
|
-
error('type-cast-structured', [ art.type.location, art ], {},
|
|
525
|
-
'Structured elements can\'t be cast to a different type');
|
|
526
|
-
}
|
|
519
|
+
if (art.type._artifact?.elements)
|
|
520
|
+
error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'to-structure' } );
|
|
521
|
+
else if (elem.elements) // TODO: calc elements
|
|
522
|
+
error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'from-structure' } );
|
|
527
523
|
}
|
|
528
524
|
}
|
|
529
525
|
|
|
@@ -921,30 +921,33 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
921
921
|
// cAnnoValue: the annotation value (c : csn)
|
|
922
922
|
// oTarget: the result object (o: odata)
|
|
923
923
|
// oTermName: current term
|
|
924
|
-
//
|
|
925
|
-
function handleValue(cAnnoValue, oTarget, oTermName,
|
|
924
|
+
// dTypeNameArg: expected type of cAnnoValue according to dictionary, may be null (d: dictionary)
|
|
925
|
+
function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg ) {
|
|
926
926
|
// this function basically only figures out what kind of annotation value we have
|
|
927
927
|
// (can be: array, expression, enum, pseudo-record, record, simple value),
|
|
928
928
|
// then calls a more specific function to deal with it and puts
|
|
929
929
|
// the result into the oTarget object
|
|
930
930
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
{
|
|
931
|
+
const [ dTypeName, dTypeIsACollection ] = stripCollection(dTypeNameArg);
|
|
932
|
+
|
|
933
|
+
if (Array.isArray(cAnnoValue)) {
|
|
934
|
+
if (isEnumType(dTypeName)) {
|
|
935
935
|
// if we find an array although we expect an enum, this may be a "flag enum"
|
|
936
|
-
checkMultiEnumValue(
|
|
936
|
+
checkMultiEnumValue();
|
|
937
937
|
oTarget.setJSON({ 'EnumMember': generateMultiEnumValue(cAnnoValue, false), 'EnumMember@odata.type' : '#'+dTypeName });
|
|
938
938
|
oTarget.setXml( { 'EnumMember': generateMultiEnumValue(cAnnoValue, true) });
|
|
939
939
|
}
|
|
940
|
-
else
|
|
941
|
-
|
|
942
|
-
oTarget.append(generateCollection(cAnnoValue, oTermName, dTypeName, msg));
|
|
940
|
+
else {
|
|
941
|
+
oTarget.append(generateCollection(cAnnoValue, oTermName, dTypeName, dTypeIsACollection, msg));
|
|
943
942
|
}
|
|
944
943
|
}
|
|
945
944
|
else if (cAnnoValue && typeof cAnnoValue === 'object') {
|
|
946
945
|
// an empty record is rendered as <Record/>
|
|
947
946
|
if ('=' in cAnnoValue) {
|
|
947
|
+
if (dTypeIsACollection) {
|
|
948
|
+
message('odata-anno-value', msg.location,
|
|
949
|
+
{ anno: msg.anno(), str: 'path', '#': 'incompval' });
|
|
950
|
+
}
|
|
948
951
|
// expression
|
|
949
952
|
const res = handleExpression(cAnnoValue['='], dTypeName);
|
|
950
953
|
oTarget.setXml( { [res.name] : res.value });
|
|
@@ -954,8 +957,8 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
954
957
|
const enumSymbol = cAnnoValue['#'];
|
|
955
958
|
// enum
|
|
956
959
|
if (dTypeName) {
|
|
957
|
-
const typeDef = getDictType(
|
|
958
|
-
if(typeDef && typeDef.$Allowed && !typeDef.Members) {
|
|
960
|
+
const typeDef = getDictType(dTypeName);
|
|
961
|
+
if (typeDef && typeDef.$Allowed && !typeDef.Members) {
|
|
959
962
|
const allowedValue = typeDef.$Allowed.Symbols[enumSymbol];
|
|
960
963
|
if(!allowedValue) {
|
|
961
964
|
message('odata-anno-value', msg.location,
|
|
@@ -982,7 +985,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
982
985
|
}
|
|
983
986
|
else if (cAnnoValue['$value'] !== undefined) {
|
|
984
987
|
// "pseudo-structure" used for annotating scalar annotations
|
|
985
|
-
handleValue(cAnnoValue
|
|
988
|
+
handleValue(cAnnoValue.$value, oTarget, oTermName, dTypeNameArg, msg);
|
|
986
989
|
|
|
987
990
|
const k = Object.keys(cAnnoValue).filter( x => x[0] === '@');
|
|
988
991
|
if (!k || k.length === 0) {
|
|
@@ -1005,7 +1008,12 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1005
1008
|
}
|
|
1006
1009
|
else {
|
|
1007
1010
|
// regular record
|
|
1008
|
-
|
|
1011
|
+
if (dTypeIsACollection) {
|
|
1012
|
+
message('odata-anno-value', msg.location, {
|
|
1013
|
+
anno: msg.anno(), str: 'structured', type: dTypeName, '#': 'incompval',
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
oTarget.append(generateRecord(cAnnoValue, oTermName, dTypeName, dTypeIsACollection, msg));
|
|
1009
1017
|
}
|
|
1010
1018
|
}
|
|
1011
1019
|
else {
|
|
@@ -1021,7 +1029,6 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1021
1029
|
}
|
|
1022
1030
|
oTarget.setJSON( { [res.jsonName] : res.value });
|
|
1023
1031
|
}
|
|
1024
|
-
|
|
1025
1032
|
// found an enum value ("#"), check whether this fits
|
|
1026
1033
|
// the expected type "dTypeName"
|
|
1027
1034
|
function checkEnumValue(value) {
|
|
@@ -1053,15 +1060,17 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1053
1060
|
// cAnnoValue: array
|
|
1054
1061
|
// dTypeName: expected type, already identified as enum type
|
|
1055
1062
|
// array is expected to contain enum values
|
|
1056
|
-
function checkMultiEnumValue(
|
|
1057
|
-
|
|
1063
|
+
function checkMultiEnumValue( ) {
|
|
1064
|
+
// we know that dTypeName is not null
|
|
1058
1065
|
const type = getDictType(dTypeName);
|
|
1059
|
-
if (!type || type
|
|
1066
|
+
if (!type || type.IsFlags !== 'true') {
|
|
1060
1067
|
message('odata-anno-value', msg.location,
|
|
1061
|
-
{
|
|
1068
|
+
{
|
|
1069
|
+
anno: msg.anno(),
|
|
1062
1070
|
str: 'collection',
|
|
1063
1071
|
type: dTypeName,
|
|
1064
|
-
'#': '
|
|
1072
|
+
'#': 'incompval',
|
|
1073
|
+
});
|
|
1065
1074
|
}
|
|
1066
1075
|
|
|
1067
1076
|
let index = 0;
|
|
@@ -1092,8 +1101,6 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1092
1101
|
}
|
|
1093
1102
|
}
|
|
1094
1103
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
1104
|
// found an expression value ("=") "expr"
|
|
1098
1105
|
// expected type is dTypeName
|
|
1099
1106
|
// note: expr can also be provided if an enum/complex type/collection is expected
|
|
@@ -1264,18 +1271,20 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1264
1271
|
// dTypeName : name of the expected record type according to vocabulary, may be null
|
|
1265
1272
|
//
|
|
1266
1273
|
// can be called for a record directly below a term, or at a deeper level
|
|
1267
|
-
function generateRecord(obj, termName, dTypeName, msg) {
|
|
1274
|
+
function generateRecord( obj, termName, dTypeName, dTypeIsACollection, msg ) {
|
|
1268
1275
|
/** @type {object} */
|
|
1269
1276
|
const newRecord = new Edm.Record(v);
|
|
1270
1277
|
|
|
1271
1278
|
// first determine what is the actual type to be used for the record
|
|
1272
1279
|
if (dTypeName && !isComplexType(dTypeName)) {
|
|
1273
|
-
if (!getDictType(dTypeName) && !isPrimitiveType(dTypeName) && !
|
|
1274
|
-
message('odata-anno-dict', msg.location,
|
|
1275
|
-
|
|
1276
|
-
else
|
|
1280
|
+
if (!getDictType(dTypeName) && !isPrimitiveType(dTypeName) && !dTypeIsACollection) {
|
|
1281
|
+
message('odata-anno-dict', msg.location, { anno: msg.anno(), type: dTypeName });
|
|
1282
|
+
} else {
|
|
1277
1283
|
message('odata-anno-value', msg.location,
|
|
1278
|
-
|
|
1284
|
+
{
|
|
1285
|
+
anno: msg.anno(), str: 'structured', type: dTypeName, '#': 'incompval',
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1279
1288
|
return newRecord;
|
|
1280
1289
|
}
|
|
1281
1290
|
|
|
@@ -1383,19 +1392,14 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1383
1392
|
|
|
1384
1393
|
// annoValue is an array
|
|
1385
1394
|
// dTypeName : Collection(...) according to dictionary
|
|
1386
|
-
function generateCollection(annoValue, termName, dTypeName, msg) {
|
|
1395
|
+
function generateCollection( annoValue, termName, dTypeName, dTypeIsACollection, msg ) {
|
|
1387
1396
|
const newCollection = new Edm.Collection(v);
|
|
1388
1397
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
}
|
|
1395
|
-
else {
|
|
1396
|
-
message('odata-anno-value', msg.location,
|
|
1397
|
-
{ anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'struct' });
|
|
1398
|
-
}
|
|
1398
|
+
if (dTypeName && !dTypeIsACollection) {
|
|
1399
|
+
message('odata-anno-value', msg.location,
|
|
1400
|
+
{
|
|
1401
|
+
anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'incompval',
|
|
1402
|
+
});
|
|
1399
1403
|
}
|
|
1400
1404
|
|
|
1401
1405
|
let index = 0;
|
|
@@ -1412,7 +1416,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1412
1416
|
}
|
|
1413
1417
|
else if (value && typeof value === 'object') {
|
|
1414
1418
|
if (value['=']) {
|
|
1415
|
-
const res = handleExpression(value['='],
|
|
1419
|
+
const res = handleExpression(value['='], dTypeName);
|
|
1416
1420
|
const newPropertyPath = new Edm.ValueThing(v, res.name, res.value );
|
|
1417
1421
|
newPropertyPath.setJSON( { [res.name] : res.value } );
|
|
1418
1422
|
newCollection.append(newPropertyPath);
|
|
@@ -1425,13 +1429,13 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1425
1429
|
newCollection.append(handleEdmJson(value['$edmJson'], msg));
|
|
1426
1430
|
}
|
|
1427
1431
|
else {
|
|
1428
|
-
newCollection.append(generateRecord(value, termName,
|
|
1432
|
+
newCollection.append(generateRecord(value, termName, dTypeName, dTypeIsACollection, msg));
|
|
1429
1433
|
}
|
|
1430
1434
|
}
|
|
1431
1435
|
else {
|
|
1432
|
-
const res = handleSimpleValue(value,
|
|
1433
|
-
const newThing = (value === null) ?new Edm.ValueThing(v, 'Null') : new Edm.ValueThing(v, res.name, value );
|
|
1434
|
-
newThing.setJSON( { [res.jsonName]
|
|
1436
|
+
const res = handleSimpleValue(value, dTypeName, msg);
|
|
1437
|
+
const newThing = (value === null) ? new Edm.ValueThing(v, 'Null') : new Edm.ValueThing(v, res.name, value );
|
|
1438
|
+
newThing.setJSON( { [res.jsonName]: res.value });
|
|
1435
1439
|
newCollection.append(newThing);
|
|
1436
1440
|
}
|
|
1437
1441
|
|
|
@@ -1783,23 +1787,21 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1783
1787
|
return dTypeName;
|
|
1784
1788
|
}
|
|
1785
1789
|
|
|
1786
|
-
function stripCollection(typeName) {
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
+
function stripCollection( typeName ) {
|
|
1791
|
+
if (typeName) {
|
|
1792
|
+
const match = typeName.match(/^Collection\((.+)\)/);
|
|
1793
|
+
if (match)
|
|
1794
|
+
return [ match[1], true ];
|
|
1790
1795
|
}
|
|
1791
|
-
|
|
1796
|
+
|
|
1797
|
+
return [ typeName, false ];
|
|
1792
1798
|
}
|
|
1793
1799
|
|
|
1794
1800
|
function isPrimitiveType(typeName) {
|
|
1795
1801
|
return typeName.split('.')[0] === 'Edm';
|
|
1796
1802
|
}
|
|
1797
1803
|
|
|
1798
|
-
function
|
|
1799
|
-
return typeName.match(/^Collection\((.+)\)/) !== null;
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
function isEnumType(dTypeName) {
|
|
1804
|
+
function isEnumType( dTypeName ) {
|
|
1803
1805
|
const type = getDictType(dTypeName);
|
|
1804
1806
|
return type && type['$kind'] === 'EnumType';
|
|
1805
1807
|
}
|
|
@@ -40,11 +40,11 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
40
40
|
const keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
|
|
41
41
|
if (keyNames.length === 0) {
|
|
42
42
|
keyNames.push('MISSING');
|
|
43
|
-
message('odata-anno-preproc',
|
|
44
|
-
|
|
43
|
+
message('odata-anno-preproc', assoc.$path, { anno, name: targetName, '#': 'nokey' } );
|
|
44
|
+
}
|
|
45
|
+
else if (keyNames.length > 1) {
|
|
46
|
+
message('odata-anno-preproc', assoc.$path, { anno, name: targetName, '#': 'multkeys' });
|
|
45
47
|
}
|
|
46
|
-
else if (keyNames.length > 1)
|
|
47
|
-
message('odata-anno-preproc', null, { anno, name: targetName, '#': 'multkeys' });
|
|
48
48
|
|
|
49
49
|
return keyNames[0];
|
|
50
50
|
}
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -745,7 +745,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
745
745
|
if(rt) // add EntitySet attribute only if return type is a non abstract entity
|
|
746
746
|
{
|
|
747
747
|
const definition = schemaCsn.definitions[rt];
|
|
748
|
-
if(definition && definition.kind === 'entity' && !definition.abstract)
|
|
748
|
+
if (definition && definition.kind === 'entity' && !definition.abstract && !edmUtils.isSingleton(definition))
|
|
749
749
|
{
|
|
750
750
|
actionImport.setEdmAttribute('EntitySet', edmUtils.getBaseName(rt));
|
|
751
751
|
}
|
|
@@ -1104,6 +1104,16 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
1104
1104
|
{ type:edmType, name, version: (p.v4 ? '4.0' : '2.0'), '#': 'facet' });
|
|
1105
1105
|
}
|
|
1106
1106
|
});
|
|
1107
|
+
if (edmType === 'Edm.Decimal') {
|
|
1108
|
+
const precision = Number.parseInt(p._edmAttributes.Precision, 10);
|
|
1109
|
+
const scale = Number.parseInt(p._edmAttributes.Scale, 10);
|
|
1110
|
+
if (!Number.isNaN(precision) && !Number.isNaN(scale) && scale > precision) {
|
|
1111
|
+
message('odata-spec-violation-type', pLoc,
|
|
1112
|
+
{
|
|
1113
|
+
type: edmType, number: scale, rawvalue: precision, ersion: (p.v4 ? '4.0' : '2.0'), '#': 'scale',
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1107
1117
|
}
|
|
1108
1118
|
else {
|
|
1109
1119
|
message('odata-spec-violation-type-unknown', pLoc,
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -610,7 +610,7 @@ function addTypeFacets(node, csn)
|
|
|
610
610
|
// map both floating and variable to => variable
|
|
611
611
|
if(node._edmAttributes.Scale === 'floating')
|
|
612
612
|
node.setEdmAttribute('Scale', 'variable');
|
|
613
|
-
if(
|
|
613
|
+
if (csn.precision == null && csn.scale == null)
|
|
614
614
|
// if Decimal has no p, s set scale 'variable'
|
|
615
615
|
node.setXml( { Scale: 'variable' } ); // floating is V4.01
|
|
616
616
|
}
|
|
@@ -17,8 +17,8 @@ const { setProp } = require('../../base/model');
|
|
|
17
17
|
* @param {object} parent The "parent" of which we transform a property of
|
|
18
18
|
* @param {string} prop The property of parent to start at
|
|
19
19
|
* @param {object} customTransformers Map of prop to transform and function to apply
|
|
20
|
-
* @param {Function[]} [artifactTransformers
|
|
21
|
-
* @param {applyTransformationsOptions} [options
|
|
20
|
+
* @param {Function[]} [artifactTransformers] Transformations to run on the artifacts, like forEachDefinition
|
|
21
|
+
* @param {applyTransformationsOptions} [options]
|
|
22
22
|
* @param {CSN.Path} path Path to parent
|
|
23
23
|
* @returns {object} parent with transformations applied
|
|
24
24
|
*/
|
|
@@ -165,8 +165,8 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
165
165
|
*
|
|
166
166
|
* @param {object} csn CSN to enrich in-place
|
|
167
167
|
* @param {object} customTransformers Map of _prop to transform and function to apply
|
|
168
|
-
* @param {Function[]} [artifactTransformers
|
|
169
|
-
* @param {applyTransformationsOptions} [options
|
|
168
|
+
* @param {Function[]} [artifactTransformers] Transformations to run on the artifacts, like forEachDefinition
|
|
169
|
+
* @param {applyTransformationsOptions} [options]
|
|
170
170
|
* @returns {object} CSN with transformations applied
|
|
171
171
|
*/
|
|
172
172
|
function applyTransformations( csn, customTransformers = {}, artifactTransformers = [], options = { } ) {
|
|
@@ -192,7 +192,7 @@ function applyTransformations( csn, customTransformers = {}, artifactTransformer
|
|
|
192
192
|
* @param {object} parent The "parent" of which we transform a property of
|
|
193
193
|
* @param {string} prop The property of parent to start at
|
|
194
194
|
* @param {object} customTransformers Map of prop to transform and function to apply
|
|
195
|
-
* @param {applyTransformationsOptions} [options
|
|
195
|
+
* @param {applyTransformationsOptions} [options]
|
|
196
196
|
* @param {CSN.Path} path Path pointing to parent
|
|
197
197
|
* @returns {object} parent[prop] with transformations applied
|
|
198
198
|
*/
|
|
@@ -213,7 +213,7 @@ function applyTransformationsOnNonDictionary( parent, prop, customTransformers =
|
|
|
213
213
|
*
|
|
214
214
|
* @param {object} dictionary Dictionary to enrich in-place
|
|
215
215
|
* @param {object} customTransformers Map of prop to transform and function to apply
|
|
216
|
-
* @param {applyTransformationsOptions} [options
|
|
216
|
+
* @param {applyTransformationsOptions} [options]
|
|
217
217
|
* @param {CSN.Path} path Path pointing to parent
|
|
218
218
|
* @returns {object} dictionary with transformations applied
|
|
219
219
|
*/
|
|
@@ -18,13 +18,11 @@ const { forEach } = require('../../utils/objectUtils');
|
|
|
18
18
|
* @param {CSN.Options} options
|
|
19
19
|
* @param {string} pathDelimiter
|
|
20
20
|
* @param {object} messageFunctions
|
|
21
|
-
* @param {Function} messageFunctions.error
|
|
22
|
-
* @param {Function} messageFunctions.info
|
|
23
|
-
* @param {Function} messageFunctions.throwWithAnyError
|
|
24
21
|
* @param {object} csnUtils
|
|
25
22
|
* @param {object} [iterateOptions]
|
|
26
23
|
*/
|
|
27
|
-
function expandStructureReferences( csn, options, pathDelimiter,
|
|
24
|
+
function expandStructureReferences( csn, options, pathDelimiter, messageFunctions, csnUtils, iterateOptions = {} ) {
|
|
25
|
+
const { error, info, throwWithAnyError } = messageFunctions;
|
|
28
26
|
const {
|
|
29
27
|
isStructured, get$combined, getFinalTypeInfo,
|
|
30
28
|
} = csnUtils;
|
|
@@ -32,7 +30,6 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
32
30
|
|
|
33
31
|
rewriteExpandInline();
|
|
34
32
|
|
|
35
|
-
|
|
36
33
|
applyTransformations(csn, {
|
|
37
34
|
keys: (parent, name, keys, path) => {
|
|
38
35
|
parent.keys = expand(keys, path.concat('keys'), true);
|
|
@@ -519,7 +516,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
519
516
|
*
|
|
520
517
|
* @param {Array} thing
|
|
521
518
|
* @param {CSN.Path} path
|
|
522
|
-
* @param {boolean} [withAlias
|
|
519
|
+
* @param {boolean} [withAlias] Whether to "expand" the (implicit) alias aswell.
|
|
523
520
|
* @returns {Array} New array - with all structured things expanded
|
|
524
521
|
*/
|
|
525
522
|
function expand( thing, path, withAlias = false ) {
|
|
@@ -538,6 +535,18 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
538
535
|
col.as = implicitAs(col.ref);
|
|
539
536
|
newThing.push(col);
|
|
540
537
|
}
|
|
538
|
+
else if (col.cast?.type) {
|
|
539
|
+
// TODO: Support expanding `null as field : StructuredT`
|
|
540
|
+
const _art = col.cast._type || inspectRef(path.concat(i, 'cast', 'type')).art;
|
|
541
|
+
if (_art && isStructured(_art)) {
|
|
542
|
+
error('type-invalid-cast', path.concat(i, 'cast', 'type'), {
|
|
543
|
+
'#': col.val !== undefined ? 'val-to-structure' : 'expr-to-structure', value: col.val,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
newThing.push(col);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
541
550
|
else {
|
|
542
551
|
newThing.push(col);
|
|
543
552
|
}
|
|
@@ -608,7 +617,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
608
617
|
*
|
|
609
618
|
* @param {object} base The raw set of things a * can expand to
|
|
610
619
|
* @param {Array} subs Things - the .expand/.inline or .columns
|
|
611
|
-
* @param {string[]} [excluding
|
|
620
|
+
* @param {string[]} [excluding]
|
|
612
621
|
* @returns {Array} If there was a star, expand it and handle shadowing/excluding, else just return subs
|
|
613
622
|
*/
|
|
614
623
|
function replaceStar( base, subs, excluding = [] ) {
|
|
@@ -192,7 +192,7 @@ function flattenAllStructStepsInRefs( csn, options, resolved, pathDelimiter, ite
|
|
|
192
192
|
* For each step of the links, check if there is a type reference.
|
|
193
193
|
* If there is, resolve it and store the result in a WeakMap.
|
|
194
194
|
*
|
|
195
|
-
* @param {Array} [links
|
|
195
|
+
* @param {Array} [links]
|
|
196
196
|
* @todo seems too hacky
|
|
197
197
|
* @returns {WeakMap} A WeakMap where a link is the key and the type is the value
|
|
198
198
|
*/
|
|
@@ -63,6 +63,8 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
|
|
|
63
63
|
if (query.SELECT && query.SELECT.where && query.SELECT.where.length > 1)
|
|
64
64
|
toProcess.push([ path.slice(0, -1), path.concat('where') ]);
|
|
65
65
|
|
|
66
|
+
if (query.SELECT && query.SELECT.having?.length > 1)
|
|
67
|
+
toProcess.push([ path.slice(0, -1), path.concat('having') ]);
|
|
66
68
|
|
|
67
69
|
if (query.SELECT && query.SELECT.columns)
|
|
68
70
|
toProcess.push([ path.slice(0, -1), path.concat('columns') ]);
|
|
@@ -98,7 +98,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
98
98
|
inspectRef,
|
|
99
99
|
artifactRef,
|
|
100
100
|
effectiveType,
|
|
101
|
-
getFinalTypeInfo
|
|
101
|
+
getFinalTypeInfo,
|
|
102
|
+
dropDefinitionCache,
|
|
103
|
+
initDefinition,
|
|
102
104
|
} = csnUtils;
|
|
103
105
|
|
|
104
106
|
// are we working with structured OData or not
|
|
@@ -147,6 +149,8 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
147
149
|
// TODO: handle artifact.projection instead of artifact.query correctly in future V2
|
|
148
150
|
if (def.kind === 'entity' && def.projection) {
|
|
149
151
|
def.query = { SELECT: def.projection };
|
|
152
|
+
dropDefinitionCache(def);
|
|
153
|
+
initDefinition(def);
|
|
150
154
|
}
|
|
151
155
|
}],
|
|
152
156
|
{ skipArtifact: isExternalServiceMember }
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
forAllQueries,
|
|
14
14
|
sortCsnDefinitionsForTests,
|
|
15
15
|
} = require('../model/csnUtils');
|
|
16
|
+
const {CompilerAssertion} = require('../base/error');
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Indicator that a definition is localized and has a convenience view.
|
|
@@ -577,14 +578,22 @@ function _addLocalizationViews(csn, options, useJoins, config) {
|
|
|
577
578
|
if (!obj || !obj.ref)
|
|
578
579
|
return;
|
|
579
580
|
const ref = Array.isArray(obj.ref) ? obj.ref[0] : obj.ref;
|
|
580
|
-
if (typeof ref
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
else
|
|
581
|
+
if (typeof ref === 'string') {
|
|
582
|
+
const def = csn.definitions[ref];
|
|
583
|
+
if (def && def[_hasLocalizedView]) {
|
|
584
|
+
if (Array.isArray(obj.ref))
|
|
585
|
+
obj.ref[0] = def[_hasLocalizedView];
|
|
586
|
+
else
|
|
587
587
|
obj.ref = def[_hasLocalizedView];
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
} else if (ref.id) {
|
|
591
|
+
const def = csn.definitions[ref.id];
|
|
592
|
+
if (def && def[_hasLocalizedView])
|
|
593
|
+
obj.ref[0].id = def[_hasLocalizedView];
|
|
594
|
+
|
|
595
|
+
} else if (options.testMode) {
|
|
596
|
+
throw new CompilerAssertion('Debug me: Unhandled reference during localized-rewrite!');
|
|
588
597
|
}
|
|
589
598
|
}
|
|
590
599
|
|
|
@@ -730,7 +730,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
730
730
|
// this will substitute multiple backlink conditions ($self = ... AND $self = ...AND ...)
|
|
731
731
|
if(expr.op) {
|
|
732
732
|
let x = clone(expr);
|
|
733
|
-
|
|
733
|
+
if(expr.args)
|
|
734
|
+
x.args = expr.args.map(cloneOnCondition);
|
|
734
735
|
return x;
|
|
735
736
|
}
|
|
736
737
|
|
|
@@ -328,9 +328,9 @@ module.exports = (csn, options) => {
|
|
|
328
328
|
* passed to this function.
|
|
329
329
|
*
|
|
330
330
|
* @param {CSN.Element} member
|
|
331
|
-
* @param {object} [except
|
|
331
|
+
* @param {object} [except] List of properties which should not be propagated along the origin chain
|
|
332
332
|
* of the `member`
|
|
333
|
-
* @param {object} [force
|
|
333
|
+
* @param {object} [force] Overwrite any member propagation rules or any except and always propagate the corresponding keys
|
|
334
334
|
*/
|
|
335
335
|
function propagateMemberPropsFromOrigin( member, except = null, force = null ) {
|
|
336
336
|
const memberChain = getOriginChain(member);
|
|
@@ -613,8 +613,8 @@ module.exports = (csn, options) => {
|
|
|
613
613
|
* @param {object} to
|
|
614
614
|
* @param {Function} getCustomRule getter for the `memberProps` or `defProps`
|
|
615
615
|
* which shall be used for retrieving custom rules
|
|
616
|
-
* @param {object} [except
|
|
617
|
-
* @param {object} [force
|
|
616
|
+
* @param {object} [except] array of properties which should not be propagated
|
|
617
|
+
* @param {object} [force] Force propagation of the contained keys via a custom rule.
|
|
618
618
|
*/
|
|
619
619
|
function copyProperties( from, to, getCustomRule, except = null, force = null ) {
|
|
620
620
|
const keys = Object.keys(from);
|