@sap/cds-compiler 6.6.2 → 6.7.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 +37 -1
- package/bin/cdsc.js +2 -0
- package/bin/cdsse.js +1 -1
- package/lib/base/message-registry.js +13 -7
- package/lib/base/model.js +0 -72
- package/lib/checks/elements.js +1 -1
- package/lib/checks/featureFlags.js +2 -2
- package/lib/checks/manyExpand.js +28 -0
- package/lib/checks/validator.js +2 -0
- package/lib/compiler/assert-consistency.js +3 -4
- package/lib/compiler/base.js +8 -0
- package/lib/compiler/builtins.js +8 -9
- package/lib/compiler/checks.js +27 -6
- package/lib/compiler/cycle-detector.js +4 -4
- package/lib/compiler/define.js +65 -83
- package/lib/compiler/extend.js +357 -325
- package/lib/compiler/finalize-parse-cdl.js +3 -4
- package/lib/compiler/generate.js +205 -203
- package/lib/compiler/kick-start.js +34 -49
- package/lib/compiler/populate.js +95 -28
- package/lib/compiler/propagator.js +3 -5
- package/lib/compiler/resolve.js +17 -13
- package/lib/compiler/shared.js +52 -20
- package/lib/compiler/tweak-assocs.js +2 -4
- package/lib/compiler/utils.js +84 -31
- package/lib/edm/edmAnnoPreprocessor.js +2 -2
- package/lib/gen/BaseParser.js +924 -1055
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +5 -2
- package/lib/json/from-csn.js +25 -16
- package/lib/main.d.ts +13 -0
- package/lib/model/revealInternalProperties.js +18 -0
- package/lib/parsers/AstBuildingParser.js +22 -5
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toSql.js +1 -1
- package/lib/render/utils/sql.js +2 -2
- package/lib/render/utils/standardDatabaseFunctions.js +2 -2
- package/lib/transform/db/constraints.js +3 -4
- package/lib/transform/db/expansion.js +2 -44
- package/lib/transform/db/killAnnotations.js +1 -1
- package/lib/transform/db/processSqlServices.js +10 -11
- package/lib/transform/effective/main.js +2 -1
- package/lib/transform/featureFlags.js +6 -1
- package/lib/transform/forOdata.js +7 -124
- package/lib/transform/forRelationalDB.js +2 -1
- package/lib/transform/odata/fioriTreeViews.js +173 -0
- package/lib/transform/odata/flattening.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +7 -4
- package/package.json +1 -1
- package/share/messages/message-explanations.json +0 -2
- package/share/messages/type-unexpected-foreign-keys.md +0 -52
- package/share/messages/type-unexpected-on-condition.md +0 -52
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
f0320d5264a6107c245a82427b4f6338
|
package/lib/gen/CdlParser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// Parser generated by redepage v0.3.
|
|
1
|
+
// Parser generated by redepage v0.3.2
|
|
2
2
|
'use strict;'
|
|
3
3
|
const { XsnSource, XsnArtifact, XsnName } = require( '../compiler/xsn-model' )
|
|
4
|
+
const { isValidEnumValue } = require( '../compiler/base' )
|
|
4
5
|
const AstBuildingParser = require('../parsers/AstBuildingParser')
|
|
5
6
|
const keywords={
|
|
6
7
|
abstract:0,
|
|
@@ -2717,7 +2718,9 @@ case'@':this.annoAssignStd({art},336);continue
|
|
|
2717
2718
|
default:this.s=337;continue
|
|
2718
2719
|
}
|
|
2719
2720
|
case 337:switch(this.lk()){
|
|
2720
|
-
case'Id':case'@':case'key':this.elementDef({outer:$.outer,art},0)
|
|
2721
|
+
case'Id':case'@':case'key':if(this.elementDef({outer:$.outer,art},0)){ if (![ 'virtual', 'key', 'masked', '$syntax', 'elements', 'type', 'items' ]
|
|
2722
|
+
.some( p => art[p] ) && isValidEnumValue( art.value ))
|
|
2723
|
+
art.$syntax = 'or-enum'; }continue
|
|
2721
2724
|
case'extend':this.ckP(338,['Id']);continue
|
|
2722
2725
|
default:this.ei();continue
|
|
2723
2726
|
}
|
package/lib/json/from-csn.js
CHANGED
|
@@ -121,6 +121,7 @@ const { isAnnotationExpression } = require('../base/builtins');
|
|
|
121
121
|
const { CompilerAssertion } = require('../base/error');
|
|
122
122
|
const { Location } = require('../base/location');
|
|
123
123
|
const { XsnSource } = require('../compiler/xsn-model');
|
|
124
|
+
const { isValidEnumValue } = require( '../compiler/base' );
|
|
124
125
|
const { xsnAsTree, splitClauses } = require('../parsers/XprTree');
|
|
125
126
|
|
|
126
127
|
const $location = Symbol.for('cds.$location');
|
|
@@ -734,7 +735,7 @@ const schema = compileSchema( {
|
|
|
734
735
|
class: 'expression', // calculated elements
|
|
735
736
|
vZeroFor: 'val', // CSN v0.1.0 property for `val` in enum def
|
|
736
737
|
// type: annoValue,
|
|
737
|
-
inKind: [ 'element'
|
|
738
|
+
inKind: [ 'element' ],
|
|
738
739
|
optional: exprProperties.concat([ 'stored' ]),
|
|
739
740
|
},
|
|
740
741
|
stored: {
|
|
@@ -993,21 +994,15 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
993
994
|
}
|
|
994
995
|
pushLocation( def );
|
|
995
996
|
const savedInExtensions = inExtensions;
|
|
996
|
-
|
|
997
|
-
const
|
|
997
|
+
const r = { location: location(), kind: undefined };
|
|
998
|
+
const kind = calculateKind( def, spec, r ); // might set inExtensions
|
|
999
|
+
if (kind !== '$column')
|
|
1000
|
+
r.kind ??= kind;
|
|
998
1001
|
const xor = {};
|
|
999
1002
|
const { prop } = spec;
|
|
1000
1003
|
const kind0 = (spec.validKinds.length || spec.prop === 'extensions') && kind;
|
|
1001
1004
|
const csnProps = Object.keys( def );
|
|
1002
1005
|
|
|
1003
|
-
// For compatibility, extension property `elements` could actually be an `enum`:
|
|
1004
|
-
if (savedInExtensions === '' && prop === 'elements' && // in extend property `elements`
|
|
1005
|
-
!Object.keys( def ).some( couldNotBeEnumProperty )) {
|
|
1006
|
-
r.$syntax = 'enum'; // could be an enum
|
|
1007
|
-
if (def.val !== undefined || def['#'] !== undefined)
|
|
1008
|
-
kind = 'enum'; // for function expected(), i.e. allow property `val`/`#`
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
1006
|
if (csnProps.length) {
|
|
1012
1007
|
const valueName = (prop === 'keys' || prop === 'foreignKeys' ? 'targetElement' : 'value');
|
|
1013
1008
|
// the next is basically object() + the inValue handling
|
|
@@ -1057,17 +1052,19 @@ function namespace( ref, spec ) {
|
|
|
1057
1052
|
return ns ? { kind: 'namespace', name: ns } : null;
|
|
1058
1053
|
}
|
|
1059
1054
|
|
|
1060
|
-
function
|
|
1061
|
-
// returns true for `value` (which we allow with warning when extending an enum with `elements`)
|
|
1055
|
+
function couldBeEnumProperty( prop ) {
|
|
1062
1056
|
const inKind = schema[prop]?.inKind; // undefined for annotations, $location, …
|
|
1063
1057
|
// inKind for annotation assignments is function -> can be for enum
|
|
1064
|
-
return
|
|
1058
|
+
return inKind && // `value` handled extra
|
|
1059
|
+
(!Array.isArray( inKind ) || prop === 'value' || inKind.includes( 'enum' ));
|
|
1065
1060
|
}
|
|
1066
1061
|
|
|
1067
1062
|
function actions( def, spec, xsn, csn, name ) {
|
|
1068
1063
|
if (def.kind === 'extend' && (def.elements || def.enum)) {
|
|
1069
1064
|
// TODO: Handle this case in extend.js; already done for `returns`
|
|
1070
1065
|
// See message ext-expecting-returns
|
|
1066
|
+
// TODO: the location would also bad here
|
|
1067
|
+
// (should be at the property, not containing object - but we remove this anyway)
|
|
1071
1068
|
error( 'syntax-unexpected-property', location(true), {
|
|
1072
1069
|
'#': def.kind,
|
|
1073
1070
|
prop: def.enum ? 'enum' : 'elements',
|
|
@@ -1873,7 +1870,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1873
1870
|
return { type: ignore };
|
|
1874
1871
|
}
|
|
1875
1872
|
|
|
1876
|
-
function calculateKind( def, spec ) {
|
|
1873
|
+
function calculateKind( def, spec, xsn ) {
|
|
1877
1874
|
if (inExtensions) {
|
|
1878
1875
|
inExtensions = spec.defaultKind;
|
|
1879
1876
|
return 'annotate';
|
|
@@ -1883,9 +1880,21 @@ function calculateKind( def, spec ) {
|
|
|
1883
1880
|
return (def.extend) ? 'extend' : 'annotate';
|
|
1884
1881
|
}
|
|
1885
1882
|
const kind = (def.kind === 'view') ? 'entity' : def.kind; // 'view' is CSN v0.1.0
|
|
1886
|
-
|
|
1883
|
+
const inExtend = inExtensions === '';
|
|
1884
|
+
if (inExtend && kind === 'extend') // extend in extend -> keep inExtensions
|
|
1887
1885
|
return 'extend';
|
|
1888
1886
|
inExtensions = null;
|
|
1887
|
+
// Compatibilitity: object rendered in `elements` of extend might be enum:
|
|
1888
|
+
if (inExtend && !def.kind && spec.defaultKind === 'element' &&
|
|
1889
|
+
Object.keys( def ).every( couldBeEnumProperty )) {
|
|
1890
|
+
if (isValidEnumValue( def.value ))
|
|
1891
|
+
xsn.$syntax = 'or-enum'; // could be an enum
|
|
1892
|
+
xsn.kind = 'element';
|
|
1893
|
+
// We also allow a `val` which is not embedded in a `value` also on an element
|
|
1894
|
+
// def inside extensions, because that is how compiler v3 has written it:
|
|
1895
|
+
if (def.val && !def.value)
|
|
1896
|
+
return 'enum'; // use schema of enum (for `val`)
|
|
1897
|
+
}
|
|
1889
1898
|
if (!spec.validKinds.includes( kind ))
|
|
1890
1899
|
return spec.defaultKind;
|
|
1891
1900
|
return (def.abstract || def.$syntax === 'aspect')
|
package/lib/main.d.ts
CHANGED
|
@@ -313,6 +313,19 @@ declare namespace compiler {
|
|
|
313
313
|
* @private
|
|
314
314
|
*/
|
|
315
315
|
edm4OpenAPI?: boolean
|
|
316
|
+
/**
|
|
317
|
+
* Enable the draft-generated artifacts to handle state messages.
|
|
318
|
+
*
|
|
319
|
+
* @since v6.1.0
|
|
320
|
+
*/
|
|
321
|
+
draftMessages?: boolean
|
|
322
|
+
/**
|
|
323
|
+
* Add `CreatedByUserDescription`, `LastChangedByUserDescription` and
|
|
324
|
+
* `InProcessByUserDescription` fields to the `DraftAdministrativeData` entity.
|
|
325
|
+
*
|
|
326
|
+
* @since v6.6.0
|
|
327
|
+
*/
|
|
328
|
+
draftUserDescription?: boolean
|
|
316
329
|
}
|
|
317
330
|
|
|
318
331
|
/**
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// This function should return a meaningful result in all circumstances:
|
|
9
9
|
// * with --parse-only, with both CDL and CSN input,
|
|
10
10
|
// * for the core compiler output and all transformations working on the XSN.
|
|
11
|
+
//
|
|
12
|
+
// TODO: make sure that all extend statements are listed)
|
|
11
13
|
|
|
12
14
|
'use strict';
|
|
13
15
|
|
|
@@ -95,6 +97,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
95
97
|
_layerExtends: layerExtends,
|
|
96
98
|
_origin: origin,
|
|
97
99
|
$compositionTargets: d => d, // dictionary( boolean )
|
|
100
|
+
_extensions: revealExtensions,
|
|
98
101
|
_extend: reveal,
|
|
99
102
|
_annotate: reveal,
|
|
100
103
|
_annotateS: artifactIdentifier,
|
|
@@ -270,6 +273,21 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
270
273
|
return artifactDictionary( node, parent );
|
|
271
274
|
}
|
|
272
275
|
|
|
276
|
+
function revealExtensions( node ) {
|
|
277
|
+
if (Array.isArray( node ))
|
|
278
|
+
return array( node, revealSingleExtension );
|
|
279
|
+
const r = {};
|
|
280
|
+
for (const prop in node)
|
|
281
|
+
r[prop] = revealExtensions( node[prop] );
|
|
282
|
+
return r;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function revealSingleExtension( node, parent ) {
|
|
286
|
+
return (node.kind && node.__unique_id__ == null && node.$effectiveSeqNo == null && !node.builtin)
|
|
287
|
+
? reveal( node, parent )
|
|
288
|
+
: artifactIdentifier( node, parent );
|
|
289
|
+
}
|
|
290
|
+
|
|
273
291
|
function reveal( node, parent, name ) {
|
|
274
292
|
if (node == null || typeof node !== 'object' )
|
|
275
293
|
return node;
|
|
@@ -39,6 +39,17 @@ const valueTokens = {
|
|
|
39
39
|
true: true,
|
|
40
40
|
};
|
|
41
41
|
const valueTokensLength = Object.values( valueTokens ).length;
|
|
42
|
+
// likewise, if expectedArray contains all the following tokens, replace them by 'Literal'
|
|
43
|
+
const literalTokens = {
|
|
44
|
+
Number: true,
|
|
45
|
+
QuotedLiteral: true,
|
|
46
|
+
String: true,
|
|
47
|
+
'#': true,
|
|
48
|
+
true: true,
|
|
49
|
+
false: true,
|
|
50
|
+
null: true,
|
|
51
|
+
};
|
|
52
|
+
const literalTokensLength = Object.values( literalTokens ).length;
|
|
42
53
|
|
|
43
54
|
const extensionDicts = {
|
|
44
55
|
elements: true, enum: true, params: true, returns: true,
|
|
@@ -107,9 +118,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
107
118
|
let array = this.expectingArray_();
|
|
108
119
|
this.s = savedState;
|
|
109
120
|
// Avoid clutter in messages: replace all value tokens by ‹Value› if all are there:
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
array = [ 'Value', ...
|
|
121
|
+
const withoutValueTokens = raw ? array : array.filter( tok => valueTokens[tok] !== true );
|
|
122
|
+
if (withoutValueTokens.length + valueTokensLength === array.length) {
|
|
123
|
+
array = [ 'Value', ...withoutValueTokens ];
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
const withoutLiteralTokens = raw ? array : array.filter( tok => literalTokens[tok] !== true );
|
|
127
|
+
if (withoutLiteralTokens.length + literalTokensLength === array.length)
|
|
128
|
+
array = [ 'Literal', ...withoutLiteralTokens ];
|
|
129
|
+
}
|
|
113
130
|
return array.map( tok => this.antlrName( tok ) )
|
|
114
131
|
.sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
|
|
115
132
|
}
|
|
@@ -153,7 +170,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
153
170
|
this.conditionStackLength == null) // after error recovery
|
|
154
171
|
return false;
|
|
155
172
|
if (this.constructor.tracingParser)
|
|
156
|
-
this._traceSubPush( 'queryOnLeft' );
|
|
173
|
+
this._traceSubPush?.( 'queryOnLeft' );
|
|
157
174
|
return this.queryOnLeft( 'table', mode );
|
|
158
175
|
}
|
|
159
176
|
|
|
@@ -175,7 +192,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
175
192
|
if (this.inSameRule_( this.s, this.stack.at( -1 ).followState ))
|
|
176
193
|
return false;
|
|
177
194
|
this.dynamic_.parenthesesCtx = [ null ];
|
|
178
|
-
this._tracePush( 'Parentheses()' );
|
|
195
|
+
this._tracePush?.( 'Parentheses()' );
|
|
179
196
|
}
|
|
180
197
|
const { parenthesesCtx } = this.dynamic_;
|
|
181
198
|
const noQuery = parenthesesCtx?.[0];
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -4,7 +4,7 @@ const {
|
|
|
4
4
|
getLastPartOf, getLastPartOfRef,
|
|
5
5
|
hasValidSkipOrExists, getNormalizedQuery,
|
|
6
6
|
getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement,
|
|
7
|
-
pathName, hasPersistenceSkipAnnotation, implicitAs,
|
|
7
|
+
pathName, hasPersistenceSkipAnnotation, implicitAs, forEachDefinition,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const { isBuiltinType, isMagicVariable } = require('../base/builtins');
|
|
10
10
|
const keywords = require('../base/keywords');
|
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
renderReferentialConstraint,
|
|
19
19
|
} = require('./utils/sql');
|
|
20
20
|
const DuplicateChecker = require('./DuplicateChecker');
|
|
21
|
-
const {
|
|
21
|
+
const { isDeprecatedEnabled } = require('../base/model');
|
|
22
22
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
23
23
|
const { timetrace } = require('../utils/timetrace');
|
|
24
24
|
|
package/lib/render/toSql.js
CHANGED
|
@@ -104,7 +104,7 @@ class SqlRenderEnvironment {
|
|
|
104
104
|
* }
|
|
105
105
|
* }
|
|
106
106
|
*
|
|
107
|
-
* @param {CSN.Model} csn
|
|
107
|
+
* @param {CSN.Model} csn for SQL transformed CSN
|
|
108
108
|
* @param {CSN.Options} options Transformation options
|
|
109
109
|
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
110
110
|
* @returns {object} Dictionary of artifact-type:artifacts, where artifacts is a dictionary of name:content
|
package/lib/render/utils/sql.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const { getResultingName } = require('../../model/csnUtils');
|
|
6
6
|
const { smartId, delimitedId } = require('../../sql-identifier');
|
|
7
7
|
const { ModelError } = require('../../base/error');
|
|
8
|
-
const {
|
|
8
|
+
const { setProp } = require('../../base/model');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Render a given referential constraint as part of a SQL CREATE TABLE statement, or as .hdbconstraint artefact.
|
|
@@ -161,7 +161,7 @@ const allowedHdbProjectionViewProperties = {
|
|
|
161
161
|
* @returns {boolean} `true` if the artifact is a projection view, otherwise `false`.
|
|
162
162
|
*/
|
|
163
163
|
function isProjectionView( csn, artifact, options ) {
|
|
164
|
-
if (!
|
|
164
|
+
if (!artifact.projection || options.sqlDialect !== 'hana' || artifact.params || !artifact.$dataProductService)
|
|
165
165
|
return false;
|
|
166
166
|
|
|
167
167
|
if (artifact.$isProjectionView !== undefined)
|
|
@@ -666,7 +666,7 @@ const hanaFunctions = {
|
|
|
666
666
|
const x = this.renderArgs({ ...signature, args: [ args[0] ] });
|
|
667
667
|
const y = this.renderArgs({ ...signature, args: [ args[1] ] });
|
|
668
668
|
// make sure to cast NUMERIC to BIGINT (corresponds to cds.Int64)
|
|
669
|
-
return `(EXTRACT(EPOCH FROM (${ y }) - (${ x })) * 10000000)::BIGINT`;
|
|
669
|
+
return `(EXTRACT(EPOCH FROM (${ y })::TIMESTAMP - (${ x })::TIMESTAMP) * 10000000)::BIGINT`;
|
|
670
670
|
},
|
|
671
671
|
seconds_between(signature) {
|
|
672
672
|
const { args } = signature;
|
|
@@ -674,7 +674,7 @@ const hanaFunctions = {
|
|
|
674
674
|
const x = this.renderArgs({ ...signature, args: [ args[0] ] });
|
|
675
675
|
const y = this.renderArgs({ ...signature, args: [ args[1] ] });
|
|
676
676
|
|
|
677
|
-
return `EXTRACT(EPOCH FROM (${ y }) - (${ x }))::BIGINT`;
|
|
677
|
+
return `EXTRACT(EPOCH FROM (${ y })::TIMESTAMP - (${ x })::TIMESTAMP)::BIGINT`;
|
|
678
678
|
},
|
|
679
679
|
days_between(signature) {
|
|
680
680
|
const { args } = signature;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { forEachDefinition } = require('../../base/model');
|
|
4
3
|
const { applyTransformations, getResultingName, hasPersistenceSkipAnnotation } = require('../../model/csnUtils');
|
|
5
4
|
const { forEach, forEachKey } = require('../../utils/objectUtils');
|
|
6
5
|
const { CompilerAssertion } = require('../../base/error');
|
|
@@ -92,7 +91,7 @@ function createReferentialConstraints( csn, options ) {
|
|
|
92
91
|
associations.forEach(association => association.fn());
|
|
93
92
|
|
|
94
93
|
// Step III: Create the final referential constraints from all dependent key <-> parent key pairs stemming from the same $sourceAssociation
|
|
95
|
-
|
|
94
|
+
forEach(csn.definitions, collectAndAttachReferentialConstraints);
|
|
96
95
|
|
|
97
96
|
/**
|
|
98
97
|
* Retrieve the <up_> link of an `cds.Composition` used in an on-condition like `$self = <comp>.<up_>`
|
|
@@ -501,10 +500,10 @@ function createReferentialConstraints( csn, options ) {
|
|
|
501
500
|
* - Find all other elements in artifact with the same sourceAssociation
|
|
502
501
|
* - Create constraints with the information supplied by $parentKey, $parentTable and $onDelete
|
|
503
502
|
*
|
|
504
|
-
* @param {CSN.Artifact} artifact
|
|
505
503
|
* @param {string} artifactName
|
|
504
|
+
* @param {CSN.Artifact} artifact
|
|
506
505
|
*/
|
|
507
|
-
function collectAndAttachReferentialConstraints(
|
|
506
|
+
function collectAndAttachReferentialConstraints( artifactName, artifact ) {
|
|
508
507
|
const referentialConstraints = Object.create(null);
|
|
509
508
|
|
|
510
509
|
// tenant foreign keys may have multiple parent keys
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
applyTransformations,
|
|
5
|
-
setDependencies,
|
|
6
5
|
walkCsnPath,
|
|
7
6
|
getUtils,
|
|
8
7
|
forEachDefinition,
|
|
9
8
|
} = require('../../model/csnUtils');
|
|
10
9
|
const { implicitAs, columnAlias, pathId } = require('../../model/csnRefs');
|
|
11
10
|
const { setProp } = require('../../base/model');
|
|
12
|
-
const { forEach } = require('../../utils/objectUtils');
|
|
13
11
|
const { killNonrequiredAnno } = require('./killAnnotations');
|
|
14
12
|
const { featureFlags } = require('../featureFlags');
|
|
15
13
|
const { applyTransformationsOnNonDictionary } = require('./applyTransformations');
|
|
@@ -128,15 +126,11 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
128
126
|
* For such skipped things, error for usage of assoc pointing to them and ignore publishing of assoc pointing to them.
|
|
129
127
|
*/
|
|
130
128
|
function rewriteExpandInline() {
|
|
131
|
-
let cleanup = [];
|
|
132
|
-
let _dependents;
|
|
133
|
-
|
|
134
129
|
const entity = findAnEntity();
|
|
135
130
|
const toDummify = [];
|
|
136
131
|
|
|
137
132
|
applyTransformations(csn, {
|
|
138
|
-
columns: (parent
|
|
139
|
-
const artifact = csn.definitions[path[1]];
|
|
133
|
+
columns: (parent) => {
|
|
140
134
|
// get$combined expects a SET/SELECT - so we wrap the parent
|
|
141
135
|
// (which is the thing inside SET/SELECT)
|
|
142
136
|
// We can directly use SELECT here, as only projections and SELECT can have .columns
|
|
@@ -146,18 +140,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
146
140
|
root[key] = root[key][0].element;
|
|
147
141
|
});
|
|
148
142
|
const rewritten = rewrite(root, parent.columns, parent.excluding);
|
|
149
|
-
|
|
150
|
-
* Do not remove unexpandable many columns in OData
|
|
151
|
-
*/
|
|
152
|
-
if (rewritten.toMany.length > 0 && !options.toOdata) {
|
|
153
|
-
markAsToDummify(artifact, path[1]);
|
|
154
|
-
rewritten.toMany.forEach(({ art }) => {
|
|
155
|
-
error( null, art.$path || [ 'definitions', path[1] ], { name: `${ art.$env || path[1] }:${ art.ref.map(r => r.id || r) }` }, 'Unexpected .expand with to-many association $(NAME)');
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
parent.columns = rewritten.columns;
|
|
160
|
-
}
|
|
143
|
+
parent.columns = rewritten.columns;
|
|
161
144
|
},
|
|
162
145
|
});
|
|
163
146
|
|
|
@@ -166,8 +149,6 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
166
149
|
if (!options.toOdata)
|
|
167
150
|
dummyfy();
|
|
168
151
|
|
|
169
|
-
cleanup.forEach(fn => fn());
|
|
170
|
-
|
|
171
152
|
csnUtils = getUtils(csn);
|
|
172
153
|
|
|
173
154
|
const publishing = [];
|
|
@@ -267,29 +248,6 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
267
248
|
info(null, path, { name: last, target }, 'Ignoring association $(NAME) with target $(TARGET), because it was skipped because of .expand in conjunction with to-many');
|
|
268
249
|
}
|
|
269
250
|
|
|
270
|
-
/**
|
|
271
|
-
* Mark the given artifact and all (transitively) dependent artifacts as `toDummify`.
|
|
272
|
-
* This means that they will be replaced with simple dummy views in `@dummify`
|
|
273
|
-
*
|
|
274
|
-
* @param {CSN.Artifact} artifact
|
|
275
|
-
* @param {string} name
|
|
276
|
-
*/
|
|
277
|
-
function markAsToDummify( artifact, name ) {
|
|
278
|
-
if (!_dependents && cleanup.length === 0)
|
|
279
|
-
({ cleanup, _dependents } = setDependencies(csn, csnUtils));
|
|
280
|
-
|
|
281
|
-
const stack = [ [ artifact, name ] ];
|
|
282
|
-
while (stack.length > 0) {
|
|
283
|
-
const [ a, n ] = stack.pop();
|
|
284
|
-
if (a[_dependents]) {
|
|
285
|
-
forEach(a[_dependents], (dependentName, dependent) => {
|
|
286
|
-
stack.push([ dependent, dependentName ]);
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
toDummify.push(n);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
251
|
/**
|
|
294
252
|
* Replace the artifacts in `toDummify` with simple dummy views as produced by createDummyView.
|
|
295
253
|
*/
|
|
@@ -26,7 +26,7 @@ const requiredAnnos = {
|
|
|
26
26
|
'@Core.Computed': true,
|
|
27
27
|
[sqlServiceAnnotation]: true,
|
|
28
28
|
'@cds.external': true, // for external ABAP SQL services and data products for now
|
|
29
|
-
'@
|
|
29
|
+
'@data.product': true, // for data product production
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -18,13 +18,13 @@ function processSqlServices(csn, options) {
|
|
|
18
18
|
setProp(csn, '$dummyServiceEntities', Object.create(null));
|
|
19
19
|
setProp(csn, '$dataProductEntities', Object.create(null));
|
|
20
20
|
return function findAndMarkSqlServiceArtifacts(artifact, artifactName) {
|
|
21
|
-
const { sqlServiceName, dummyServiceName,
|
|
21
|
+
const { sqlServiceName, dummyServiceName, dataProductProductionServiceName } = isEntityInSqlService(artifact, artifactName, csn, options);
|
|
22
22
|
if (sqlServiceName?.length > 0)
|
|
23
23
|
setProp(artifact, '$sqlService', sqlServiceName);
|
|
24
24
|
if (dummyServiceName?.length > 0)
|
|
25
25
|
setProp(artifact, '$dummyService', dummyServiceName);
|
|
26
|
-
if (
|
|
27
|
-
setProp(artifact, '$dataProductService',
|
|
26
|
+
if (dataProductProductionServiceName?.length > 0)
|
|
27
|
+
setProp(artifact, '$dataProductService', dataProductProductionServiceName);
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -58,7 +58,7 @@ function isDummyService(artifact, options) {
|
|
|
58
58
|
* @returns {object} An object containing the names of the SQL service and external ABAP SQL service, if found.
|
|
59
59
|
*/
|
|
60
60
|
function isEntityInSqlService(artifact, artifactName, csn, options) {
|
|
61
|
-
const result = { sqlServiceName: undefined, dummyServiceName: undefined,
|
|
61
|
+
const result = { sqlServiceName: undefined, dummyServiceName: undefined, dataProductProductionServiceName: undefined };
|
|
62
62
|
if (artifact.kind !== 'entity' || !artifactName.includes('.') || hasPersistenceSkipAnnotation(artifact))
|
|
63
63
|
return result;
|
|
64
64
|
|
|
@@ -75,8 +75,8 @@ function isEntityInSqlService(artifact, artifactName, csn, options) {
|
|
|
75
75
|
if (isDummyService(definition, options))
|
|
76
76
|
result.dummyServiceName = possibleServiceName;
|
|
77
77
|
|
|
78
|
-
if (
|
|
79
|
-
result.
|
|
78
|
+
if (isDataProductProductionService(definition))
|
|
79
|
+
result.dataProductProductionServiceName = possibleServiceName;
|
|
80
80
|
|
|
81
81
|
// We don't allow nested services/contexts - if we find one, we don't need to keep searching
|
|
82
82
|
if (definition.kind === 'service' || definition.kind === 'context')
|
|
@@ -115,21 +115,20 @@ function createServiceDummy(artifact, artifactName, csn, { error }) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* Determines if the given artifact is a data product service.
|
|
118
|
+
* Determines if the given artifact is a data product production service.
|
|
119
119
|
*
|
|
120
120
|
* @param {object} artifact - The artifact to evaluate.
|
|
121
|
-
* @param {object} options - The options object containing feature flags.
|
|
122
121
|
* @returns {boolean} - Returns `true` if the artifact is a data product service, otherwise `false`.
|
|
123
122
|
*/
|
|
124
|
-
function
|
|
125
|
-
return
|
|
123
|
+
function isDataProductProductionService(artifact) {
|
|
124
|
+
return artifact.kind === 'service' && artifact['@data.product'] && !artifact['@cds.external'];
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
module.exports = {
|
|
129
128
|
processSqlServices,
|
|
130
129
|
isSqlService,
|
|
131
130
|
isDummyService,
|
|
132
|
-
|
|
131
|
+
isDataProductProductionService,
|
|
133
132
|
sqlServiceAnnotation,
|
|
134
133
|
createServiceDummy,
|
|
135
134
|
};
|
|
@@ -17,7 +17,7 @@ const misc = require('./misc');
|
|
|
17
17
|
const annotations = require('./annotations');
|
|
18
18
|
const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
|
|
19
19
|
const { cloneFullCsn } = require('../../model/cloneCsn');
|
|
20
|
-
const { featureFlags } = require('../featureFlags');
|
|
20
|
+
const { featureFlags, removeFeatureFlags } = require('../featureFlags');
|
|
21
21
|
const getServiceFilterFunction = require('./service');
|
|
22
22
|
const { traverseQuery } = require('../../model/csnRefs');
|
|
23
23
|
const { expandWildcard } = require('../db/expansion');
|
|
@@ -136,6 +136,7 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
messageFunctions.throwWithError();
|
|
139
|
+
removeFeatureFlags(csn);
|
|
139
140
|
|
|
140
141
|
return csn;
|
|
141
142
|
}
|