@sap/cds-compiler 2.12.0 → 2.15.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 +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
package/lib/compiler/utils.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
// Please do not add functions “for completeness”, this is not an API file for
|
|
7
7
|
// others but only by the core compiler.
|
|
8
8
|
|
|
9
|
+
// TODO: probably split this file into utils/….js
|
|
10
|
+
|
|
9
11
|
'use strict';
|
|
10
12
|
|
|
11
13
|
const { dictAdd, pushToDict } = require('../base/dictionaries');
|
|
@@ -31,6 +33,8 @@ function annotationIsFalse( anno ) { // falsy, but not null (u
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/**
|
|
36
|
+
* Set compiler-calculated annotation value.
|
|
37
|
+
*
|
|
34
38
|
* @param {XSN.Artifact} art
|
|
35
39
|
* @param {string} anno
|
|
36
40
|
* @param {XSN.Location} [location]
|
|
@@ -44,11 +48,11 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
|
|
|
44
48
|
name: { path: [ { id: anno.slice(1), location } ], location },
|
|
45
49
|
val,
|
|
46
50
|
literal,
|
|
51
|
+
$inferred: '$generated',
|
|
47
52
|
location,
|
|
48
53
|
};
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
// TODO: define setLink() like the current setProp(), we might have setArtifactLink()
|
|
52
56
|
// Do not share this function with CSN processors!
|
|
53
57
|
|
|
54
58
|
// The link (_artifact,_effectiveType,...) usually has the artifact as value.
|
|
@@ -57,28 +61,13 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
|
|
|
57
61
|
// - null: no valid reference, param:true if that is not allowed
|
|
58
62
|
// - false (only complete ref): multiple definitions, rejected
|
|
59
63
|
// - 0 (for _effectiveType only): circular reference
|
|
60
|
-
function setLink( obj,
|
|
64
|
+
function setLink( obj, prop, value ) {
|
|
61
65
|
Object.defineProperty( obj, prop, { value, configurable: true, writable: true } );
|
|
62
66
|
return value;
|
|
63
67
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
* It's important to set enumerable explicitly to false (although 'false' is the default),
|
|
68
|
-
* as else, if the property already exists, it keeps the old setting for enumerable.
|
|
69
|
-
*
|
|
70
|
-
* @param {object} obj
|
|
71
|
-
* @param {string} prop
|
|
72
|
-
* @param {any} value
|
|
73
|
-
*/
|
|
74
|
-
function setProp(obj, prop, value) {
|
|
75
|
-
const descriptor = {
|
|
76
|
-
value,
|
|
77
|
-
configurable: true,
|
|
78
|
-
writable: true,
|
|
79
|
-
enumerable: false,
|
|
80
|
-
};
|
|
81
|
-
Object.defineProperty( obj, prop, descriptor );
|
|
68
|
+
// And a variant with the most common `prop`:
|
|
69
|
+
function setArtifactLink( obj, value ) {
|
|
70
|
+
Object.defineProperty( obj, '_artifact', { value, configurable: true, writable: true } );
|
|
82
71
|
return value;
|
|
83
72
|
}
|
|
84
73
|
|
|
@@ -92,7 +81,7 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
|
92
81
|
elem.name.$inferred = origin.name.$inferred;
|
|
93
82
|
if (parent)
|
|
94
83
|
setMemberParent( elem, name, parent, prop ); // TODO: redef in template
|
|
95
|
-
|
|
84
|
+
setLink( elem, '_origin', origin );
|
|
96
85
|
// TODO: should we use silent dependencies also for other things, like
|
|
97
86
|
// included elements? (Currently for $inferred: 'expand-element' only)
|
|
98
87
|
if (silentDep)
|
|
@@ -110,20 +99,21 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
110
99
|
p[prop] = Object.create(null);
|
|
111
100
|
dictAdd( p[prop], name, elem );
|
|
112
101
|
}
|
|
113
|
-
if (parent._outer)
|
|
102
|
+
if (parent._outer && parent._outer.items) // TODO: remove for items, too
|
|
114
103
|
parent = parent._outer;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
setLink( elem, '_parent', parent );
|
|
105
|
+
setLink( elem, '_main', parent._main || parent );
|
|
106
|
+
const parentName = parent.name || parent._outer.name;
|
|
107
|
+
elem.name.absolute = parentName.absolute;
|
|
118
108
|
if (name == null)
|
|
119
109
|
return;
|
|
120
110
|
const normalized = kindProperties[elem.kind].normalized || elem.kind;
|
|
121
111
|
[ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
|
|
122
112
|
if (normalized === kind)
|
|
123
|
-
elem.name[kind] = (
|
|
113
|
+
elem.name[kind] = (parentName[kind] != null && kind !== 'select' && kind !== 'alias') ? `${ parentName[kind] }.${ name }` : name;
|
|
124
114
|
|
|
125
|
-
else if (
|
|
126
|
-
elem.name[kind] =
|
|
115
|
+
else if (parentName[kind] != null)
|
|
116
|
+
elem.name[kind] = parentName[kind];
|
|
127
117
|
|
|
128
118
|
else
|
|
129
119
|
delete elem.name[kind];
|
|
@@ -140,7 +130,7 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
140
130
|
*/
|
|
141
131
|
function dependsOn( user, art, location ) {
|
|
142
132
|
if (!user._deps)
|
|
143
|
-
|
|
133
|
+
setLink( user, '_deps', [] );
|
|
144
134
|
user._deps.push( { art, location } );
|
|
145
135
|
}
|
|
146
136
|
|
|
@@ -153,17 +143,17 @@ function dependsOn( user, art, location ) {
|
|
|
153
143
|
*/
|
|
154
144
|
function dependsOnSilent( user, art ) {
|
|
155
145
|
if (!user._deps)
|
|
156
|
-
|
|
146
|
+
setLink( user, '_deps', [] );
|
|
157
147
|
user._deps.push( { art } );
|
|
158
148
|
}
|
|
159
149
|
|
|
160
150
|
function storeExtension( elem, name, prop, parent, block ) {
|
|
161
151
|
if (prop === 'enum')
|
|
162
152
|
prop = 'elements';
|
|
163
|
-
|
|
153
|
+
setLink( elem, '_block', block );
|
|
164
154
|
const kind = `_${ elem.kind }`; // _extend or _annotate
|
|
165
155
|
if (!parent[kind])
|
|
166
|
-
|
|
156
|
+
setLink( parent, kind, {} );
|
|
167
157
|
// if (name === '' && prop === 'params') {
|
|
168
158
|
// pushToDict( parent[kind], 'returns', elem ); // not really a dict
|
|
169
159
|
// return;
|
|
@@ -218,6 +208,174 @@ function augmentPath( location, ...args ) {
|
|
|
218
208
|
return { path: args.map( id => ({ id, location }) ), location };
|
|
219
209
|
}
|
|
220
210
|
|
|
211
|
+
function copyExpr( expr, location, skipUnderscored, rewritePath ) {
|
|
212
|
+
if (!expr || typeof expr !== 'object')
|
|
213
|
+
return expr;
|
|
214
|
+
else if (Array.isArray(expr))
|
|
215
|
+
return expr.map( e => copyExpr( e, location, skipUnderscored, rewritePath ) );
|
|
216
|
+
|
|
217
|
+
const proto = Object.getPrototypeOf( expr );
|
|
218
|
+
if (proto && proto !== Object.prototype) // do not copy object from special classes
|
|
219
|
+
return expr;
|
|
220
|
+
const r = Object.create( proto );
|
|
221
|
+
for (const prop of Object.getOwnPropertyNames( expr )) {
|
|
222
|
+
const pd = Object.getOwnPropertyDescriptor( expr, prop );
|
|
223
|
+
if (!pd.enumerable) { // should include all properties starting with _
|
|
224
|
+
if (!skipUnderscored ||
|
|
225
|
+
prop === '_artifact' || prop === '_navigation' || prop === '_effectiveType')
|
|
226
|
+
Object.defineProperty( r, prop, pd );
|
|
227
|
+
}
|
|
228
|
+
else if (!proto) {
|
|
229
|
+
r[prop] = copyExpr( pd.value, location, skipUnderscored, rewritePath );
|
|
230
|
+
}
|
|
231
|
+
else if (prop === 'location') {
|
|
232
|
+
r[prop] = location || pd.value;
|
|
233
|
+
}
|
|
234
|
+
else if (prop.charAt(0) !== '$' || prop === '$inferred') {
|
|
235
|
+
r[prop] = copyExpr( pd.value, location, skipUnderscored, rewritePath );
|
|
236
|
+
}
|
|
237
|
+
else if (!skipUnderscored) { // skip $ properties
|
|
238
|
+
Object.defineProperty( r, prop, pd );
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return r;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function testExpr( expr, pathTest, queryTest ) {
|
|
245
|
+
// TODO: also check path arguments/filters
|
|
246
|
+
if (!expr || typeof expr === 'string') { // parse error or keywords in {xpr:...}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
else if (Array.isArray(expr)) {
|
|
250
|
+
return expr.some( e => testExpr( e, pathTest, queryTest ) );
|
|
251
|
+
}
|
|
252
|
+
else if (expr.path) {
|
|
253
|
+
return pathTest( expr );
|
|
254
|
+
}
|
|
255
|
+
else if (expr.query) {
|
|
256
|
+
return queryTest( expr.query );
|
|
257
|
+
}
|
|
258
|
+
else if (expr.op && expr.args) {
|
|
259
|
+
// unnamed args => array
|
|
260
|
+
if (Array.isArray(expr.args))
|
|
261
|
+
return expr.args.some( e => testExpr( e, pathTest, queryTest ) );
|
|
262
|
+
// named args => dictionary
|
|
263
|
+
for (const namedArg of Object.keys(expr.args)) {
|
|
264
|
+
if (testExpr(expr.args[namedArg], pathTest, queryTest))
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Return true if the path `item` with a final type `assoc` has a max target
|
|
272
|
+
// cardinality greater than one - either specified on the path item or assoc type.
|
|
273
|
+
function targetMaxNotOne( assoc, item ) {
|
|
274
|
+
// Semantics of associations without provided cardinality: [*,0..1]
|
|
275
|
+
const cardinality = item.cardinality || assoc.cardinality;
|
|
276
|
+
return cardinality && cardinality.targetMax && cardinality.targetMax.val !== 1;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Query tree post-order traversal - called for everything which contributes to the query
|
|
280
|
+
// i.e. is necessary to calculate the elements of the query
|
|
281
|
+
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
282
|
+
// NOTE: does not run on non-referred sub queries! Consider using ‹main›.$queries instead!
|
|
283
|
+
function traverseQueryPost( query, simpleOnly, callback ) {
|
|
284
|
+
if (!query) // parser error
|
|
285
|
+
return;
|
|
286
|
+
if (!query.op) { // in FROM (not JOIN)
|
|
287
|
+
if (query.query) // subquery
|
|
288
|
+
traverseQueryPost( query.query, simpleOnly, callback );
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (simpleOnly) {
|
|
292
|
+
const { from } = query;
|
|
293
|
+
if (!from || from.join) // parse error or join
|
|
294
|
+
return; // ok are: path or simple sub query (!)
|
|
295
|
+
}
|
|
296
|
+
if (query.from) { // SELECT
|
|
297
|
+
traverseQueryPost( query.from, simpleOnly, callback );
|
|
298
|
+
// console.log('FC:')
|
|
299
|
+
callback( query );
|
|
300
|
+
// console.log('FE:')
|
|
301
|
+
}
|
|
302
|
+
else if (query.args) { // JOIN, UNION, INTERSECT
|
|
303
|
+
if (!query.join && simpleOnly == null) {
|
|
304
|
+
// enough for elements: traverse only first args for UNION/INTERSECT
|
|
305
|
+
// TODO: we might use this also when we do not rewrite associations
|
|
306
|
+
// in non-referred sub queries
|
|
307
|
+
traverseQueryPost( query.args[0], simpleOnly, callback );
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
for (const q of query.args)
|
|
311
|
+
traverseQueryPost( q, simpleOnly, callback );
|
|
312
|
+
// The ON condition has to be traversed extra, because it must be evaluated
|
|
313
|
+
// after the complete FROM has been traversed. It is also not necessary to
|
|
314
|
+
// evaluate it in populateQuery().
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// else: with parse error (`select from <EOF>`, `select distinct from;`)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Call callback on all queries in dependency order, i.e. starting with query Q
|
|
321
|
+
// 1. sub queries in FROM sources of Q
|
|
322
|
+
// 2. Q itself, except if non-referred query, but with right UNION parts
|
|
323
|
+
// 3. sub queries in ON in FROM of Q
|
|
324
|
+
// 4. sub queries in columns, WHERE, HAVING
|
|
325
|
+
function traverseQueryExtra( main, callback ) {
|
|
326
|
+
if (!main.$queries)
|
|
327
|
+
return;
|
|
328
|
+
// with a top-level UNION, $queries[0] is just the left
|
|
329
|
+
traverseQueryPost( main.query, false, (q) => { // also with right of UNION (to be compatible)
|
|
330
|
+
setLink( q, '_status', 'extra' );
|
|
331
|
+
callback( q );
|
|
332
|
+
} );
|
|
333
|
+
for (const query of main.$queries.slice(1)) {
|
|
334
|
+
if (query._status === 'extra' || query._parent.kind === '$tableAlias')
|
|
335
|
+
continue; // if parent is alias, query is FROM source -> run by traverseQueryPost
|
|
336
|
+
// we are now in the top-level (parent is entity) or a non-referred query (parent is query)
|
|
337
|
+
setLink( query, '_status', 'extra' ); // do not call callback() in non-referred query
|
|
338
|
+
// console.log( 'A:', query.name,query._status)
|
|
339
|
+
traverseQueryPost( query, null, (q) => {
|
|
340
|
+
if (q._status !== 'extra') {
|
|
341
|
+
// console.log( 'T:', q.name)
|
|
342
|
+
setLink( q, '_status', 'extra' );
|
|
343
|
+
callback( q );
|
|
344
|
+
}
|
|
345
|
+
// else console.log( 'E:', q.name)
|
|
346
|
+
} );
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// About Helper property $expand for faster the XSN-to-CSN transformation
|
|
351
|
+
// - null/undefined: artifact, member, items does not contain expanded members
|
|
352
|
+
// - 'origin': all expanded (sub) elements have no new target/on and no new annotations
|
|
353
|
+
// that value is only on elements, types, and params -> no other members
|
|
354
|
+
// when set, only on elem/art with expanded elements
|
|
355
|
+
// - 'target': all expanded (sub) elements might only have new target/on, but
|
|
356
|
+
// no indivual annotations on any (sub) member
|
|
357
|
+
// when set, traverse all parents where the value has been 'origin' before
|
|
358
|
+
// - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
359
|
+
// not counting propagated ones; set up to the definition (main artifact)
|
|
360
|
+
// (only set with anno on $inferred elem), annotate “beats” target
|
|
361
|
+
// Usage according to CSN flavor:
|
|
362
|
+
// - gensrc: do not render inferred elements (including expanded elements),
|
|
363
|
+
// collect annotate statements with value 'annotate'
|
|
364
|
+
// - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
365
|
+
// has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
366
|
+
// (might sometimes render the elements unnecessarily, which is not wrong)
|
|
367
|
+
// - universal: do not render expanded sub elements if $expand = 'origin'
|
|
368
|
+
function setExpandStatus( elem, status ) {
|
|
369
|
+
// set on element
|
|
370
|
+
while (elem._main) {
|
|
371
|
+
elem = elem._parent;
|
|
372
|
+
if (status === 'annotate' ? elem.$expand === 'annotate' : elem.$expand !== 'origin')
|
|
373
|
+
return;
|
|
374
|
+
elem.$expand = status; // meaning: expanded, containing assocs
|
|
375
|
+
for (let line = elem.items; line; line = line.items)
|
|
376
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
377
|
+
}
|
|
378
|
+
}
|
|
221
379
|
|
|
222
380
|
module.exports = {
|
|
223
381
|
pushLink,
|
|
@@ -225,7 +383,7 @@ module.exports = {
|
|
|
225
383
|
annotationIsFalse,
|
|
226
384
|
annotateWith,
|
|
227
385
|
setLink,
|
|
228
|
-
|
|
386
|
+
setArtifactLink,
|
|
229
387
|
linkToOrigin,
|
|
230
388
|
dependsOn,
|
|
231
389
|
dependsOnSilent,
|
|
@@ -235,4 +393,10 @@ module.exports = {
|
|
|
235
393
|
pathName,
|
|
236
394
|
augmentPath,
|
|
237
395
|
splitIntoPath,
|
|
396
|
+
copyExpr,
|
|
397
|
+
testExpr,
|
|
398
|
+
targetMaxNotOne,
|
|
399
|
+
traverseQueryPost,
|
|
400
|
+
traverseQueryExtra,
|
|
401
|
+
setExpandStatus,
|
|
238
402
|
};
|
|
@@ -4,15 +4,16 @@ const edmUtils = require('../edmUtils.js');
|
|
|
4
4
|
const preprocessAnnotations = require('./preprocessAnnotations.js');
|
|
5
5
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
6
6
|
const { forEachDefinition } = require('../../model/csnUtils');
|
|
7
|
+
const { forEach } = require("../../utils/objectUtils");
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
/*
|
|
10
|
-
|
|
10
|
+
/*
|
|
11
11
|
OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/master/vocabularies
|
|
12
12
|
Aggregation (published)
|
|
13
13
|
Authorization (published)
|
|
14
14
|
Capabilities (published)
|
|
15
15
|
Core (published)
|
|
16
|
+
JSON (published)
|
|
16
17
|
Measures (published)
|
|
17
18
|
Repeatability (published)
|
|
18
19
|
Temporal (not published, not yet finalized)
|
|
@@ -23,8 +24,9 @@ const { forEachDefinition } = require('../../model/csnUtils');
|
|
|
23
24
|
CodeList (published)
|
|
24
25
|
Common (pubished)
|
|
25
26
|
Communication (published)
|
|
27
|
+
DataIntegration (published)
|
|
26
28
|
Graph (published, experimental)
|
|
27
|
-
Hierarchy (
|
|
29
|
+
Hierarchy (published, experimental)
|
|
28
30
|
HTML5 (published, experimental)
|
|
29
31
|
ODM (published, experimental)
|
|
30
32
|
PersonalData (published)
|
|
@@ -73,16 +75,31 @@ const vocabularyDefinitions = {
|
|
|
73
75
|
'inc': { Alias: 'Core', Namespace: 'Org.OData.Core.V1' },
|
|
74
76
|
'int': { filename: 'Core.xml' }
|
|
75
77
|
},
|
|
78
|
+
'DataIntegration': {
|
|
79
|
+
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/DataIntegration.xml' },
|
|
80
|
+
'inc': { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
|
|
81
|
+
'int': { filename: 'DataIntegration.xml' }
|
|
82
|
+
},
|
|
76
83
|
'Graph': {
|
|
77
84
|
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
|
|
78
85
|
'inc': { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
|
|
79
86
|
'int': { filename: 'Graph.xml' }
|
|
80
87
|
},
|
|
88
|
+
'Hierarchy': {
|
|
89
|
+
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Hierarchy.xml' },
|
|
90
|
+
'inc': { Alias: 'Hierarchy', Namespace: 'com.sap.vocabularies.Hierarchy.v1' },
|
|
91
|
+
'int': { filename: 'Hierarchy.xml' }
|
|
92
|
+
},
|
|
81
93
|
'HTML5': {
|
|
82
94
|
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml' },
|
|
83
95
|
'inc': { Alias: 'HTML5', Namespace: 'com.sap.vocabularies.HTML5.v1' },
|
|
84
96
|
'int': { filename: 'HTML5.xml' }
|
|
85
97
|
},
|
|
98
|
+
'JSON': {
|
|
99
|
+
'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.JSON.V1.xml' },
|
|
100
|
+
'inc': { Alias: 'JSON', Namespace: 'Org.OData.JSON.V1' },
|
|
101
|
+
'int': { filename: 'JSON.xml' }
|
|
102
|
+
},
|
|
86
103
|
'Measures': {
|
|
87
104
|
'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml' },
|
|
88
105
|
'inc': { Alias: 'Measures', Namespace: 'Org.OData.Measures.V1' },
|
|
@@ -414,7 +431,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
414
431
|
// do nothing
|
|
415
432
|
|
|
416
433
|
if(!isEdmPropertyRendered(carrier, options) ||
|
|
417
|
-
(isV2() && (edmUtils.isDerivedType(carrier)
|
|
434
|
+
(isV2() && (edmUtils.isDerivedType(carrier)))) {
|
|
418
435
|
return;
|
|
419
436
|
}
|
|
420
437
|
|
|
@@ -504,7 +521,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
504
521
|
let hasAlternativeCarrier = false; // is the alternative annotation target available in the EDM?
|
|
505
522
|
let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
|
|
506
523
|
|
|
507
|
-
if (carrier.kind === 'entity'
|
|
524
|
+
if (carrier.kind === 'entity') {
|
|
508
525
|
// If AppliesTo=[EntitySet/Singleton/Collection, EntityType], EntitySet/Singleton/Collection has precedence
|
|
509
526
|
testToAlternativeEdmTarget = (x => {
|
|
510
527
|
if(options.isV2()) {
|
|
@@ -554,7 +571,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
554
571
|
}
|
|
555
572
|
else {
|
|
556
573
|
// this might be more precise if handleAnnotation would know more about the carrier
|
|
557
|
-
testToStandardEdmTarget = (x => x
|
|
574
|
+
testToStandardEdmTarget = (x => x
|
|
558
575
|
? ['Parameter', 'Property'].some(y => x.includes(y) ||
|
|
559
576
|
carrier._isCollection && x.includes('Collection'))
|
|
560
577
|
: true);
|
|
@@ -663,7 +680,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
663
680
|
}
|
|
664
681
|
mergePathStepsIntoPrefixTree(tree[name], pathSteps, index+1, carrier);
|
|
665
682
|
}
|
|
666
|
-
else {
|
|
683
|
+
else if(typeof tree === 'object' ){
|
|
667
684
|
tree[name] = carrier['@' + pathSteps.join('.')];
|
|
668
685
|
}
|
|
669
686
|
}
|
|
@@ -714,9 +731,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
714
731
|
* @type {object}
|
|
715
732
|
* */
|
|
716
733
|
let newAnno = undefined;
|
|
717
|
-
const
|
|
734
|
+
const omissions = { 'Aggregation.default':1 };
|
|
735
|
+
const nullList = { 'Core.OperationAvailable':1 };
|
|
718
736
|
const voc = termName.slice(0, termName.indexOf('.'));
|
|
719
|
-
if(vocabularyDefinitions[voc] && annoValue !== null ||
|
|
737
|
+
if(vocabularyDefinitions[voc] && annoValue !== null && !omissions[termName]|| nullList[termName]) {
|
|
720
738
|
newAnno = new Edm.Annotation(v, termName);
|
|
721
739
|
|
|
722
740
|
// termName may contain a qualifier: @UI.FieldGroup#shippingStatus
|
|
@@ -729,8 +747,8 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
729
747
|
message(error, context,
|
|
730
748
|
`OData annotation qualifier "${ p[1] }" must start with a letter or underscore, followed by at most 127 letters, underscores or digits`);
|
|
731
749
|
}
|
|
732
|
-
newAnno.Term
|
|
733
|
-
newAnno.Qualifier
|
|
750
|
+
newAnno.setEdmAttribute('Term', termNameWithoutQualifiers);
|
|
751
|
+
newAnno.setEdmAttribute('Qualifier', p[1]);
|
|
734
752
|
}
|
|
735
753
|
if (p.length>2) {
|
|
736
754
|
message(warning, context, `multiple qualifiers (${ p[1] },${ p[2] }${ p.length > 3 ? ',...' : '' })`);
|
|
@@ -948,7 +966,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
948
966
|
if (typeof val === 'string') {
|
|
949
967
|
if (dTypeName === 'Edm.Boolean') {
|
|
950
968
|
typeName = 'Bool';
|
|
951
|
-
if (
|
|
969
|
+
if (val !== 'true' && val !== 'false') {
|
|
952
970
|
message(warning, context, `found String, but expected type ${ dTypeName }`);
|
|
953
971
|
}
|
|
954
972
|
}
|
|
@@ -1072,7 +1090,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1072
1090
|
// this type doesn't exist
|
|
1073
1091
|
message(warning, context, `explicitly specified type '${ actualTypeName }' not found in vocabulary`);
|
|
1074
1092
|
// explicitly mentioned type, render in XML and JSON
|
|
1075
|
-
newRecord.Type
|
|
1093
|
+
newRecord.setEdmAttribute('Type', actualTypeName);
|
|
1076
1094
|
}
|
|
1077
1095
|
else if (dTypeName && !isDerivedFrom(actualTypeName, dTypeName)) {
|
|
1078
1096
|
// this type doesn't fit the expected one
|
|
@@ -1080,7 +1098,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1080
1098
|
}' is not derived from expected type '${ dTypeName }'`);
|
|
1081
1099
|
actualTypeName = dTypeName;
|
|
1082
1100
|
// explicitly mentioned type, render in XML and JSON
|
|
1083
|
-
newRecord.Type
|
|
1101
|
+
newRecord.setEdmAttribute('Type', actualTypeName);
|
|
1084
1102
|
}
|
|
1085
1103
|
else if (isAbstractType(actualTypeName)) {
|
|
1086
1104
|
// this type is abstract
|
|
@@ -1088,7 +1106,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1088
1106
|
if(dTypeName)
|
|
1089
1107
|
actualTypeName = dTypeName;
|
|
1090
1108
|
// set to definition name and render in XML and JSON
|
|
1091
|
-
newRecord.Type
|
|
1109
|
+
newRecord.setEdmAttribute('Type', actualTypeName);
|
|
1092
1110
|
}
|
|
1093
1111
|
else {
|
|
1094
1112
|
// ok
|
|
@@ -1251,7 +1269,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1251
1269
|
const props = Object.create(null);
|
|
1252
1270
|
Object.entries(obj).forEach(([k, val]) => {
|
|
1253
1271
|
if(k === '@type') {
|
|
1254
|
-
edmNode.Type
|
|
1272
|
+
edmNode.setEdmAttribute('Type', val);
|
|
1255
1273
|
}
|
|
1256
1274
|
else {
|
|
1257
1275
|
let child = undefined;
|
|
@@ -1284,14 +1302,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1284
1302
|
});
|
|
1285
1303
|
}
|
|
1286
1304
|
else { // literal
|
|
1287
|
-
let escaped = obj;
|
|
1288
|
-
if (typeof escaped === 'string') {
|
|
1289
|
-
escaped = escaped.replace(/&/g, '&')
|
|
1290
|
-
}
|
|
1291
1305
|
edmNode = new Edm.ValueThing(v,
|
|
1292
|
-
exprDef && exprDef.valueThingName || getXmlTypeName(
|
|
1306
|
+
exprDef && exprDef.valueThingName || getXmlTypeName(obj), obj);
|
|
1293
1307
|
// typename for static expression rendering
|
|
1294
|
-
edmNode.setJSON( { [getJsonTypeName(
|
|
1308
|
+
edmNode.setJSON( { [getJsonTypeName(obj)]: obj } );
|
|
1295
1309
|
}
|
|
1296
1310
|
}
|
|
1297
1311
|
}
|
|
@@ -1301,14 +1315,14 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1301
1315
|
edmNode = exprDef.create(obj);
|
|
1302
1316
|
|
|
1303
1317
|
// iterate over each obj.property and translate expression into EDM
|
|
1304
|
-
|
|
1318
|
+
forEach(obj, (name, val) => {
|
|
1305
1319
|
if(exprDef) {
|
|
1306
1320
|
if(exprDef.anno && name[0] === '@') {
|
|
1307
1321
|
edmNode.append(handleTerm(name.slice(1), val, context));
|
|
1308
1322
|
}
|
|
1309
1323
|
else if (exprDef.attr && exprDef.attr.includes(name)) {
|
|
1310
1324
|
if (name[0] === '$') {
|
|
1311
|
-
edmNode
|
|
1325
|
+
edmNode.setEdmAttribute(name.slice(1), val);
|
|
1312
1326
|
}
|
|
1313
1327
|
}
|
|
1314
1328
|
else if (exprDef.jsonAttr && exprDef.jsonAttr.includes(name)) {
|
|
@@ -101,7 +101,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
101
101
|
|
|
102
102
|
// inner functions
|
|
103
103
|
function draftAnnotations(carrier, aName, aNameWithoutQualifier) {
|
|
104
|
-
if ((carrier.kind === 'entity'
|
|
104
|
+
if ((carrier.kind === 'entity') &&
|
|
105
105
|
(aNameWithoutQualifier === '@Common.DraftRoot.PreparationAction' ||
|
|
106
106
|
aNameWithoutQualifier === '@Common.DraftRoot.ActivationAction' ||
|
|
107
107
|
aNameWithoutQualifier === '@Common.DraftRoot.EditAction' ||
|
|
@@ -136,7 +136,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
136
136
|
return false;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
if (carrier.kind === 'entity'
|
|
139
|
+
if (carrier.kind === 'entity') {
|
|
140
140
|
warning(null, null, `annotation preprocessing/${aNameWithoutQualifier}: annotation must not be used for an entity, ${ctx}`);
|
|
141
141
|
return false;
|
|
142
142
|
}
|