@sap/cds-compiler 2.12.0 → 2.13.6
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 +110 -15
- package/bin/cdsc.js +13 -13
- 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 +28 -63
- package/lib/api/options.js +3 -3
- package/lib/api/validate.js +0 -5
- 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 +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- 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 +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +32 -13
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -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 +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- 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 +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- package/lib/transform/db/views.js +5 -4
- 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 +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- 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
|
};
|
|
@@ -504,7 +504,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
504
504
|
let hasAlternativeCarrier = false; // is the alternative annotation target available in the EDM?
|
|
505
505
|
let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
|
|
506
506
|
|
|
507
|
-
if (carrier.kind === 'entity'
|
|
507
|
+
if (carrier.kind === 'entity') {
|
|
508
508
|
// If AppliesTo=[EntitySet/Singleton/Collection, EntityType], EntitySet/Singleton/Collection has precedence
|
|
509
509
|
testToAlternativeEdmTarget = (x => {
|
|
510
510
|
if(options.isV2()) {
|
|
@@ -554,7 +554,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
554
554
|
}
|
|
555
555
|
else {
|
|
556
556
|
// this might be more precise if handleAnnotation would know more about the carrier
|
|
557
|
-
testToStandardEdmTarget = (x => x
|
|
557
|
+
testToStandardEdmTarget = (x => x
|
|
558
558
|
? ['Parameter', 'Property'].some(y => x.includes(y) ||
|
|
559
559
|
carrier._isCollection && x.includes('Collection'))
|
|
560
560
|
: true);
|
|
@@ -948,7 +948,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
948
948
|
if (typeof val === 'string') {
|
|
949
949
|
if (dTypeName === 'Edm.Boolean') {
|
|
950
950
|
typeName = 'Bool';
|
|
951
|
-
if (
|
|
951
|
+
if (val !== 'true' && val !== 'false') {
|
|
952
952
|
message(warning, context, `found String, but expected type ${ dTypeName }`);
|
|
953
953
|
}
|
|
954
954
|
}
|
|
@@ -1284,14 +1284,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1284
1284
|
});
|
|
1285
1285
|
}
|
|
1286
1286
|
else { // literal
|
|
1287
|
-
let escaped = obj;
|
|
1288
|
-
if (typeof escaped === 'string') {
|
|
1289
|
-
escaped = escaped.replace(/&/g, '&')
|
|
1290
|
-
}
|
|
1291
1287
|
edmNode = new Edm.ValueThing(v,
|
|
1292
|
-
exprDef && exprDef.valueThingName || getXmlTypeName(
|
|
1288
|
+
exprDef && exprDef.valueThingName || getXmlTypeName(obj), obj);
|
|
1293
1289
|
// typename for static expression rendering
|
|
1294
|
-
edmNode.setJSON( { [getJsonTypeName(
|
|
1290
|
+
edmNode.setJSON( { [getJsonTypeName(obj)]: obj } );
|
|
1295
1291
|
}
|
|
1296
1292
|
}
|
|
1297
1293
|
}
|
|
@@ -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
|
}
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -45,6 +45,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
45
45
|
let [ allServices,
|
|
46
46
|
allSchemas,
|
|
47
47
|
whatsMyServiceRootName,
|
|
48
|
+
autoexposeSchemaName,
|
|
48
49
|
options ] = initializeModel(csn, _options, messageFunctions);
|
|
49
50
|
|
|
50
51
|
const Edm = require('./edm.js')(options, error);
|
|
@@ -188,9 +189,9 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
188
189
|
fqSchemaXRef.sort((a,b) => b.length-a.length);
|
|
189
190
|
|
|
190
191
|
// Bring the schemas in alphabetical order, service first, root last
|
|
191
|
-
const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !==
|
|
192
|
-
if(subSchemaDictionary
|
|
193
|
-
sortedSchemaNames.push(
|
|
192
|
+
const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== autoexposeSchemaName && n !== serviceCsn.name).sort();
|
|
193
|
+
if(subSchemaDictionary[autoexposeSchemaName])
|
|
194
|
+
sortedSchemaNames.push(autoexposeSchemaName);
|
|
194
195
|
|
|
195
196
|
// Finally create the schemas and register them in the service.
|
|
196
197
|
LeadSchema = createSchema(subSchemaDictionary[serviceCsn.name]);
|
|
@@ -234,7 +235,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
234
235
|
Object.entries(csn.definitions).forEach(([fqName, art]) => {
|
|
235
236
|
// Identify service members by their definition name only, this allows
|
|
236
237
|
// to let the internal object.name have the sub-schema name.
|
|
237
|
-
// With nested services we must do a longest path match and check
|
|
238
|
+
// With nested services we must do a longest path match and check whether
|
|
238
239
|
// the current definition belongs to the current toplevel service definition.
|
|
239
240
|
|
|
240
241
|
// Definition is to be considered if
|
|
@@ -246,7 +247,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
246
247
|
// not marked to be ignored as schema member
|
|
247
248
|
if(mySchemaName &&
|
|
248
249
|
serviceCsn.name === whatsMyServiceRootName(fqName, false) &&
|
|
249
|
-
|
|
250
|
+
art.kind !== 'context' && art.kind !== 'service') {
|
|
250
251
|
|
|
251
252
|
// Strip the toplevel serviceName from object.name
|
|
252
253
|
// except if the schema name is the service name itself.
|
|
@@ -574,7 +575,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
574
575
|
edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
|
|
575
576
|
const paramLoc = [...actLoc, 'params', parameterName];
|
|
576
577
|
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
577
|
-
if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
|
|
578
|
+
if(param._type && !param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
|
|
578
579
|
warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
|
|
579
580
|
}
|
|
580
581
|
if(param._isCollection) {
|
|
@@ -591,8 +592,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
591
592
|
// it is safe to assume that either type or items.type are set
|
|
592
593
|
const returns = action.returns.items || action.returns;
|
|
593
594
|
let type = returns.type;
|
|
594
|
-
if(type){
|
|
595
|
-
if(!isBuiltinType(type) &&
|
|
595
|
+
if (type){
|
|
596
|
+
if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
|
|
596
597
|
const returnsLoc = [ ...actLoc, 'returns'];
|
|
597
598
|
warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
|
|
598
599
|
}
|
package/lib/edm/edm.js
CHANGED
|
@@ -137,11 +137,11 @@ module.exports = function (options, error) {
|
|
|
137
137
|
let tmpStr = '';
|
|
138
138
|
edmUtils.forAll(this, (v, p) => {
|
|
139
139
|
if (typeof this[p] !== 'object')
|
|
140
|
-
tmpStr += ' ' + p + '="' + edmUtils.
|
|
140
|
+
tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
|
|
141
141
|
});
|
|
142
142
|
edmUtils.forAll(this._xmlOnlyAttributes, (v,p) => {
|
|
143
143
|
if (typeof v !== 'object')
|
|
144
|
-
tmpStr += ' ' + p + '="' + edmUtils.
|
|
144
|
+
tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
|
|
145
145
|
});
|
|
146
146
|
return tmpStr;
|
|
147
147
|
}
|
|
@@ -599,7 +599,7 @@ module.exports = function (options, error) {
|
|
|
599
599
|
// Complex/EntityType are derived from TypeBase
|
|
600
600
|
// but have no type attribute in their CSN
|
|
601
601
|
if(typecsn.type) { // this thing has a type
|
|
602
|
-
// check
|
|
602
|
+
// check whether this is a scalar type (or array of scalar type) or a named type
|
|
603
603
|
let scalarType = undefined;
|
|
604
604
|
if(typecsn.items && typecsn.items.type &&
|
|
605
605
|
isBuiltinType(typecsn.items.type)) {
|
|
@@ -879,11 +879,12 @@ module.exports = function (options, error) {
|
|
|
879
879
|
// the V2 metadata.xml
|
|
880
880
|
// @ts-ignore
|
|
881
881
|
Property.SAP_Annotation_Attribute_WhiteList = [
|
|
882
|
-
'@sap.hierarchy.node.for',
|
|
883
|
-
'@sap.hierarchy.parent.node.for', // ->
|
|
884
|
-
'@sap.hierarchy.level.for',
|
|
885
|
-
'@sap.hierarchy.drill.state.for', // ->
|
|
886
|
-
'@sap.hierarchy.node.descendant.count.for'
|
|
882
|
+
'@sap.hierarchy.node.for', // -> sap:hierarchy-node-for
|
|
883
|
+
'@sap.hierarchy.parent.node.for', // -> sap:hierarchy-parent-node-for
|
|
884
|
+
'@sap.hierarchy.level.for', // -> sap:hierarchy-level-for
|
|
885
|
+
'@sap.hierarchy.drill.state.for', // -> sap:hierarchy-drill-state-for
|
|
886
|
+
'@sap.hierarchy.node.descendant.count.for', // -> sap:hierarchy-node-descendant-count-for
|
|
887
|
+
'@sap.parameter'
|
|
887
888
|
];
|
|
888
889
|
|
|
889
890
|
super(v, attributes, csn);
|
|
@@ -927,9 +928,7 @@ module.exports = function (options, error) {
|
|
|
927
928
|
Additionally: The attribute is named 'Default' in V2 and 'DefaultValue' in V4
|
|
928
929
|
*/
|
|
929
930
|
if(this.v4)
|
|
930
|
-
this[`Default${this.v4 ? 'Value' : ''}`] =
|
|
931
|
-
? defVal
|
|
932
|
-
: edmUtils.escapeString(defVal);
|
|
931
|
+
this[`Default${this.v4 ? 'Value' : ''}`] = defVal;
|
|
933
932
|
}
|
|
934
933
|
}
|
|
935
934
|
}
|
|
@@ -1356,7 +1355,7 @@ module.exports = function (options, error) {
|
|
|
1356
1355
|
{
|
|
1357
1356
|
let kind = this.kind;
|
|
1358
1357
|
let xml = indent + '<' + kind + this.toXMLattributes();
|
|
1359
|
-
xml += (this._value !== undefined ? '>' + edmUtils.
|
|
1358
|
+
xml += (this._value !== undefined ? '>' + edmUtils.escapeStringForText(this._value) + '</' + kind + '>' : '/>');
|
|
1360
1359
|
return xml;
|
|
1361
1360
|
}
|
|
1362
1361
|
|