@sap/cds-compiler 4.0.2 → 4.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
package/lib/compiler/populate.js
CHANGED
|
@@ -41,14 +41,15 @@ const {
|
|
|
41
41
|
linkToOrigin,
|
|
42
42
|
setMemberParent,
|
|
43
43
|
proxyCopyMembers,
|
|
44
|
-
dependsOn,
|
|
45
44
|
setExpandStatus,
|
|
46
45
|
setExpandStatusAnnotate,
|
|
46
|
+
dependsOnSilent,
|
|
47
|
+
columnRefStartsWithSelf,
|
|
47
48
|
} = require('./utils');
|
|
48
49
|
const { typeParameters } = require('./builtins');
|
|
49
50
|
|
|
50
|
-
const $inferred = Symbol.for('cds.$inferred');
|
|
51
|
-
const $location = Symbol.for('cds.$location');
|
|
51
|
+
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
52
|
+
const $location = Symbol.for( 'cds.$location' );
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* These properties are copied from specified elements.
|
|
@@ -73,6 +74,7 @@ function populate( model ) {
|
|
|
73
74
|
} = model.$messageFunctions;
|
|
74
75
|
const {
|
|
75
76
|
resolvePath,
|
|
77
|
+
nestedElements,
|
|
76
78
|
attachAndEmitValidNames,
|
|
77
79
|
initArtifact,
|
|
78
80
|
extendArtifactBefore,
|
|
@@ -81,8 +83,7 @@ function populate( model ) {
|
|
|
81
83
|
Object.assign( model.$functions, {
|
|
82
84
|
effectiveType,
|
|
83
85
|
getOrigin,
|
|
84
|
-
|
|
85
|
-
navigationEnv,
|
|
86
|
+
getInheritedProp,
|
|
86
87
|
} );
|
|
87
88
|
// let depth = 100;
|
|
88
89
|
|
|
@@ -111,12 +112,17 @@ function populate( model ) {
|
|
|
111
112
|
newAutoExposed = true; // internal error if auto-expose after here
|
|
112
113
|
return;
|
|
113
114
|
|
|
115
|
+
/** Make sure that effectiveType() is called on all members and items */
|
|
114
116
|
function traverseElementEnvironments( art ) {
|
|
115
|
-
|
|
117
|
+
let type = effectiveType( art );
|
|
118
|
+
while (type?.items)
|
|
119
|
+
type = effectiveType( type.items );
|
|
116
120
|
if (art.$queries)
|
|
117
121
|
art.$queries.forEach( traverseElementEnvironments );
|
|
118
122
|
if (art.mixin)
|
|
119
|
-
dictForEach( art.mixin,
|
|
123
|
+
dictForEach( art.mixin, effectiveType );
|
|
124
|
+
if (art.targetAspect?.elements)
|
|
125
|
+
effectiveType( art.targetAspect );
|
|
120
126
|
if (art !== art._main?._leadingQuery) // already done
|
|
121
127
|
forEachMember( art, traverseElementEnvironments );
|
|
122
128
|
}
|
|
@@ -125,37 +131,6 @@ function populate( model ) {
|
|
|
125
131
|
//--------------------------------------------------------------------------
|
|
126
132
|
// The central functions for path resolution - must work on-demand
|
|
127
133
|
//--------------------------------------------------------------------------
|
|
128
|
-
// Phase 2: call effectiveType() on-demand, which also calculates view elems
|
|
129
|
-
|
|
130
|
-
// TODO: move setting dependencies and complaining about assocs in keys
|
|
131
|
-
// to resolvePath - then remove params: location, user
|
|
132
|
-
function navigationEnv( art, location, user, assocSpec ) {
|
|
133
|
-
// = effectiveType() on from-path, TODO: should actually already part of
|
|
134
|
-
// resolvePath() on FROM
|
|
135
|
-
if (!art)
|
|
136
|
-
return undefined;
|
|
137
|
-
let type = effectiveType( art );
|
|
138
|
-
while (type?.items) // TODO: disallow navigation to many sometimes
|
|
139
|
-
type = effectiveType( type.items );
|
|
140
|
-
if (!type?.target || assocSpec === 'targetAspectOnly')
|
|
141
|
-
return type;
|
|
142
|
-
|
|
143
|
-
if (assocSpec === false) {
|
|
144
|
-
// TODO: combine this with setTargetReferenceKey&Co in getPathItem?
|
|
145
|
-
error( null, [ location, user ], {},
|
|
146
|
-
'Following an association is not allowed in an association key definition' );
|
|
147
|
-
} // TODO: else warning for assoc usage with falsy assocSpec
|
|
148
|
-
const target = resolvePath( type.target, 'target', type );
|
|
149
|
-
if (!target)
|
|
150
|
-
return target;
|
|
151
|
-
if (target && assocSpec && user)
|
|
152
|
-
dependsOn( user, target, location || user.location );
|
|
153
|
-
const effectiveTarget = effectiveType( target );
|
|
154
|
-
if (effectiveTarget === 0 && location)
|
|
155
|
-
dependsOn( user, user, (user.target || user.type || user.value || user).location );
|
|
156
|
-
// console.log('NT:',assocSpec,!!user,target)
|
|
157
|
-
return effectiveTarget;
|
|
158
|
-
}
|
|
159
134
|
|
|
160
135
|
/**
|
|
161
136
|
* Return the artifact having properties which are relevant for further name
|
|
@@ -189,10 +164,11 @@ function populate( model ) {
|
|
|
189
164
|
* artifact is an association or composition; it also does not give you the
|
|
190
165
|
* information about the technical base type of an enum.
|
|
191
166
|
*
|
|
192
|
-
* Calculating an effective association/composition
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
167
|
+
* Calculating an effective association/composition implies calculating its
|
|
168
|
+
* target entity (including redirections), but not induce calculating the
|
|
169
|
+
* target's elements. Calculating an effective structure (entities, …) does
|
|
170
|
+
* not imply calculating the effective types of its elements. Calculating an
|
|
171
|
+
* effective array does not imply calculating its effective line type.
|
|
196
172
|
*/
|
|
197
173
|
function effectiveType( art ) {
|
|
198
174
|
if (!art)
|
|
@@ -212,22 +188,34 @@ function populate( model ) {
|
|
|
212
188
|
}
|
|
213
189
|
if (art)
|
|
214
190
|
art = art._effectiveType;
|
|
215
|
-
if (art === 0)
|
|
191
|
+
if (art === 0) {
|
|
192
|
+
model.$assert = 'cycle';
|
|
193
|
+
// throw Error(`CYCLE: ${ chain.length }`);
|
|
216
194
|
return art;
|
|
217
|
-
|
|
195
|
+
}
|
|
218
196
|
chain.reverse();
|
|
219
197
|
for (const a of chain) {
|
|
198
|
+
// Ensure that the _effectiveType of the parent has been calculated. This
|
|
199
|
+
// is usually the case, but might not be for elements of anonymous target
|
|
200
|
+
// aspects. Without it, extensions/annotations might get lost.
|
|
201
|
+
// For a query and its parent (usually the query entity!), it is the other way
|
|
202
|
+
// around: to calculate the _effectiveType of the query entity, we might need
|
|
203
|
+
// to calculate the _effectiveType of a query in FROM first.
|
|
204
|
+
if (a.kind !== 'select')
|
|
205
|
+
effectiveType( a._outer || a._main && a._parent );
|
|
206
|
+
// TODO: forbid $self+$self.elem inline, see expandWildcard()
|
|
220
207
|
// Without type, value.path or _origin at beginning, link to itself:
|
|
221
208
|
extendArtifactBefore( a );
|
|
222
209
|
art = populateArtifact( a, art ) || a;
|
|
223
210
|
setLink( a, '_effectiveType', art );
|
|
224
211
|
a.$effectiveSeqNo = ++effectiveSeqNo;
|
|
212
|
+
// console.log('PE:',require('../model/revealInternalProperties').ref(a))
|
|
225
213
|
if (a.elements$ || a.enum$)
|
|
226
214
|
mergeSpecifiedElementsOrEnum( a );
|
|
227
215
|
// console.log( 'ET-DO:', effectiveSeqNo, a?.kind, a?.name, a._extensions?.elements?.length )
|
|
228
216
|
extendArtifactAfter( a ); // after setting _effectiveType (for messages)
|
|
229
217
|
if (a.typeProps$)
|
|
230
|
-
setSpecifiedElementTypeProperties(a);
|
|
218
|
+
setSpecifiedElementTypeProperties( a );
|
|
231
219
|
}
|
|
232
220
|
// console.log( 'ET-END:', art?.kind, art?.name )
|
|
233
221
|
return art;
|
|
@@ -237,13 +225,16 @@ function populate( model ) {
|
|
|
237
225
|
// Name-resolution relevant properties directly at artifact:
|
|
238
226
|
// ‹view›.elements of input must have been moved (to elements$) before!
|
|
239
227
|
// console.log('Q:',art.elements,art.enum,art.items,!!art.query)
|
|
228
|
+
// console.log('PA:',require('../model/revealInternalProperties').ref(art))
|
|
240
229
|
if (art.includes) // first version of includes via effectiveTpe()
|
|
241
230
|
art.includes.forEach( i => effectiveType( i._artifact ) );
|
|
242
231
|
if (art.elements != null || art.enum != null || art.items != null)
|
|
243
232
|
return art;
|
|
244
233
|
if (art.target) {
|
|
234
|
+
// make sure that target._artifact is set:
|
|
235
|
+
const target = resolvePath( art.target, 'target', art );
|
|
245
236
|
// try to implicitly redirect explicitly provided target:
|
|
246
|
-
if (!origEffective?.target && art.kind !== 'mixin')
|
|
237
|
+
if (target && !origEffective?.target && art.kind !== 'mixin')
|
|
247
238
|
redirectImplicitly( art, art );
|
|
248
239
|
if (!art.expand)
|
|
249
240
|
return art;
|
|
@@ -257,8 +248,14 @@ function populate( model ) {
|
|
|
257
248
|
const leading = art.$queries[0];
|
|
258
249
|
if (!leading) // parse error
|
|
259
250
|
return null;
|
|
260
|
-
|
|
261
|
-
|
|
251
|
+
if (leading._effectiveType !== undefined) {
|
|
252
|
+
// You cannot refer to a query of another artifact:
|
|
253
|
+
throw new CompilerAssertion(
|
|
254
|
+
`Unexpected _effectiveType on leading query of ${ art.name.absolute }`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
// TODO: try just return (effectiveType( leading )) === 0 ? 0 : art;
|
|
258
|
+
setLink( leading, '_effectiveType', 0 ); // relevant to detect invalid $self.*
|
|
262
259
|
populateQuery( leading );
|
|
263
260
|
setLink( leading, '_effectiveType', leading );
|
|
264
261
|
leading.$effectiveSeqNo = ++effectiveSeqNo;
|
|
@@ -313,7 +310,7 @@ function populate( model ) {
|
|
|
313
310
|
if (art._origin !== undefined)
|
|
314
311
|
return art._origin;
|
|
315
312
|
if (art.type) // not stored in _origin
|
|
316
|
-
return
|
|
313
|
+
return resolvePath( art.type, 'type', art );
|
|
317
314
|
return setLink( art, '_origin', getOriginRaw( art ) );
|
|
318
315
|
}
|
|
319
316
|
|
|
@@ -321,17 +318,23 @@ function populate( model ) {
|
|
|
321
318
|
if (!art._main) {
|
|
322
319
|
if (art.query)
|
|
323
320
|
return getOrigin( art.$queries?.[0] );
|
|
321
|
+
// TODO: if we add the `includes` mechanism, use resolveUncheckedPath() for
|
|
322
|
+
// includes here, because the accept function for includes requires the
|
|
323
|
+
// elements to have been calculated!
|
|
324
324
|
}
|
|
325
325
|
else {
|
|
326
326
|
// TODO: write checks for path in enum?
|
|
327
327
|
if (art.value?.path)
|
|
328
|
-
return resolvePath( art.value, (art.$syntax === 'calc' ? 'calc' : '
|
|
329
|
-
if (art.kind === 'select')
|
|
330
|
-
|
|
328
|
+
return resolvePath( art.value, (art.$syntax === 'calc' ? 'calc' : 'column'), art );
|
|
329
|
+
if (art.kind === 'select') {
|
|
330
|
+
const alias = dictFirst( art.$tableAliases );
|
|
331
|
+
// With parse errors, the first “alias” might be $self. Using its origin
|
|
332
|
+
// would lead to a cyclic processing dependency.
|
|
333
|
+
return (alias.kind === '$tableAlias') ? getOrigin( alias ) : null;
|
|
334
|
+
}
|
|
331
335
|
// init sets _origin for alias to sub query, only need to handle ref here:
|
|
332
336
|
if (art.kind === '$tableAlias') {
|
|
333
|
-
// do not
|
|
334
|
-
// source → we would have a deeper callstack
|
|
337
|
+
// do not call effectiveType() on the source to avoid a deeper callstack
|
|
335
338
|
const source = resolvePath( art, 'from', art._parent );
|
|
336
339
|
if (!source?._main)
|
|
337
340
|
return source; // direct entity (or undefined)
|
|
@@ -339,33 +342,19 @@ function populate( model ) {
|
|
|
339
342
|
// to call effectiveType() on the last assoc of a from ref:
|
|
340
343
|
// TODO: check this with test3/Queries/DollarSelf/CorruptedSource.err.cds
|
|
341
344
|
const assoc = effectiveType( source );
|
|
342
|
-
return
|
|
345
|
+
return assoc?.target._artifact;
|
|
343
346
|
}
|
|
344
347
|
}
|
|
345
348
|
return '';
|
|
346
349
|
}
|
|
347
350
|
|
|
348
|
-
function
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (user.kind === 'event')
|
|
354
|
-
return resolvePath( ref, 'eventType', user );
|
|
355
|
-
if (user.kind === 'param' && user._parent &&
|
|
356
|
-
(user._parent.kind === 'action' || user._parent.kind === 'function'))
|
|
357
|
-
return resolvePath( ref, 'actionParamType', user );
|
|
358
|
-
return resolvePath( ref, 'type', user );
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function getCardinality( assoc ) {
|
|
362
|
-
while (assoc?._effectiveType) {
|
|
363
|
-
// if (--depth) throw Error(`GCARD: ${ Object.keys(art) }`)
|
|
364
|
-
if (assoc.cardinality)
|
|
365
|
-
return assoc.cardinality;
|
|
366
|
-
assoc = getOrigin( assoc );
|
|
351
|
+
function getInheritedProp( art, prop ) {
|
|
352
|
+
while (art?._effectiveType) {
|
|
353
|
+
if (art[prop] !== undefined)
|
|
354
|
+
return art[prop];
|
|
355
|
+
art = getOrigin( art );
|
|
367
356
|
}
|
|
368
|
-
return
|
|
357
|
+
return undefined;
|
|
369
358
|
}
|
|
370
359
|
|
|
371
360
|
function userQuery( user ) {
|
|
@@ -404,7 +393,7 @@ function populate( model ) {
|
|
|
404
393
|
proxyCopyMembers( art, 'elements', struct.elements, art.path?.location, '$navElement' );
|
|
405
394
|
return true;
|
|
406
395
|
}
|
|
407
|
-
if (art.elements || art.kind === '$
|
|
396
|
+
if (art.elements || art.kind === '$inline' ||
|
|
408
397
|
// no element expansions for "non-proper" types like
|
|
409
398
|
// entities (as parameter types) etc:
|
|
410
399
|
struct.kind !== 'type' && struct.kind !== 'element' && struct.kind !== 'param' &&
|
|
@@ -418,7 +407,8 @@ function populate( model ) {
|
|
|
418
407
|
const location = ref && ref.location || art.location;
|
|
419
408
|
// console.log( message( null, location, art, {target:struct,art}, 'Info','EXPAND-ELEM')
|
|
420
409
|
// .toString(), Object.keys(struct.elements))
|
|
421
|
-
proxyCopyMembers( art, 'elements', struct.elements, weakLocation( location )
|
|
410
|
+
proxyCopyMembers( art, 'elements', struct.elements, weakLocation( location ),
|
|
411
|
+
null, isDeprecatedEnabled( options, 'noKeyPropagationWithExpansions' ) );
|
|
422
412
|
// Set elements expansion status (the if condition is always true, as no
|
|
423
413
|
// elements expansion will take place on artifact with existing other
|
|
424
414
|
// member property):
|
|
@@ -511,7 +501,7 @@ function populate( model ) {
|
|
|
511
501
|
//--------------------------------------------------------------------------
|
|
512
502
|
|
|
513
503
|
// TODO: delete XSN._entities
|
|
514
|
-
// TODO: delete ENTITY._from
|
|
504
|
+
// TODO: delete ENTITY._from - use _origin? instead _from[0]
|
|
515
505
|
// TODO (after on-demand ext): delete XSN.$entity
|
|
516
506
|
|
|
517
507
|
/**
|
|
@@ -533,8 +523,12 @@ function populate( model ) {
|
|
|
533
523
|
const ielem = art.elements ? art.elements[id] : art.enum[id]; // inferred element
|
|
534
524
|
const selem = art.elements$ ? art.elements$[id] : art.enum$[id]; // specified element
|
|
535
525
|
if (!selem) {
|
|
536
|
-
info( 'query-missing-element', [ ielem.name.location, art ], {
|
|
537
|
-
|
|
526
|
+
info( 'query-missing-element', [ ielem.name.location, art ], {
|
|
527
|
+
'#': ielem.kind === 'enum' ? 'enum' : 'std', id,
|
|
528
|
+
}, {
|
|
529
|
+
std: 'Element $(ID) is missing in specified elements',
|
|
530
|
+
enum: 'Enum $(ID) is missing in specified enum values',
|
|
531
|
+
} );
|
|
538
532
|
}
|
|
539
533
|
else {
|
|
540
534
|
for (const prop in selem) {
|
|
@@ -543,30 +537,28 @@ function populate( model ) {
|
|
|
543
537
|
ielem[prop] = selem[prop];
|
|
544
538
|
// required for gensrc mode of to-csn.js, otherwise the annotation
|
|
545
539
|
// may be lost during recompilation.
|
|
546
|
-
// TODO: Clarify: Should gensrc add this annotation to the column or as an
|
|
547
|
-
// annotate statement? Currently it's at the column.
|
|
548
540
|
ielem[prop].$priority = 'annotate';
|
|
549
541
|
wasAnnotated = true;
|
|
550
542
|
}
|
|
551
543
|
else if (typePropertiesFromSpecifiedElements[prop]) {
|
|
552
544
|
if (!ielem.typeProps$)
|
|
553
|
-
setLink(ielem, 'typeProps$', Object.create(null));
|
|
545
|
+
setLink( ielem, 'typeProps$', Object.create( null ) );
|
|
554
546
|
// Note: At this point in time, effectiveType() was likely not called on the
|
|
555
|
-
// element, yet. Setting it here, we can't compare it to
|
|
547
|
+
// element, yet. Setting it here, we can't compare it to its value from _origin.
|
|
556
548
|
ielem.typeProps$[prop] = selem[prop];
|
|
557
549
|
}
|
|
558
550
|
}
|
|
559
551
|
|
|
560
552
|
selem.$replacement = true;
|
|
561
553
|
if (selem.elements)
|
|
562
|
-
setLink(ielem, 'elements$', selem.elements);
|
|
554
|
+
setLink( ielem, 'elements$', selem.elements );
|
|
563
555
|
if (selem.enum)
|
|
564
|
-
setLink(ielem, 'enum$', selem.enum);
|
|
556
|
+
setLink( ielem, 'enum$', selem.enum );
|
|
565
557
|
}
|
|
566
558
|
}
|
|
567
559
|
|
|
568
560
|
if (wasAnnotated)
|
|
569
|
-
setExpandStatusAnnotate(art, 'annotate');
|
|
561
|
+
setExpandStatusAnnotate( art, 'annotate' );
|
|
570
562
|
|
|
571
563
|
// TODO: We don't check enum$, yet! We first need to fix expansion for
|
|
572
564
|
// `cast(elem as EnumType)` (see #9421)
|
|
@@ -586,8 +578,8 @@ function populate( model ) {
|
|
|
586
578
|
for (const prop in art.typeProps$) {
|
|
587
579
|
let o = art;
|
|
588
580
|
if (o._effectiveType !== 0) { // cyclic
|
|
589
|
-
while (!o[prop] && getOrigin(o))
|
|
590
|
-
o = getOrigin(o);
|
|
581
|
+
while (!o[prop] && getOrigin( o ))
|
|
582
|
+
o = getOrigin( o );
|
|
591
583
|
}
|
|
592
584
|
|
|
593
585
|
if (typePropertiesFromSpecifiedElements[prop] === 'if-undefined') {
|
|
@@ -602,17 +594,19 @@ function populate( model ) {
|
|
|
602
594
|
|
|
603
595
|
function populateQuery( query ) {
|
|
604
596
|
if (query._combined || !query.from || !query.$tableAliases)
|
|
605
|
-
// already done or $join query or parse error
|
|
597
|
+
// already done (TODO: re-check!) or $join query or parse error
|
|
606
598
|
return query;
|
|
607
|
-
setLink( query, '_combined', Object.create(null) );
|
|
599
|
+
setLink( query, '_combined', Object.create( null ) );
|
|
608
600
|
query.$inlines = [];
|
|
609
601
|
forEachGeneric( query, '$tableAliases', resolveTabRef );
|
|
610
|
-
|
|
611
602
|
initFromColumns( query, query.columns );
|
|
612
603
|
if (query.excludingDict) {
|
|
613
604
|
for (const name in query.excludingDict)
|
|
614
605
|
resolveExcluding( name, query._combined, query.excludingDict, query );
|
|
615
606
|
}
|
|
607
|
+
// TODO: should we to set some falsy values? E.g. with $self.*, cyclic from?
|
|
608
|
+
// Yes, when element names cannot fully be determined (wrong source ref,
|
|
609
|
+
// cyclic, ...) BTW, similar with `includes`
|
|
616
610
|
return query;
|
|
617
611
|
|
|
618
612
|
function resolveTabRef( alias ) {
|
|
@@ -628,7 +622,7 @@ function populate( model ) {
|
|
|
628
622
|
forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
|
|
629
623
|
if (elem.$duplicates !== true)
|
|
630
624
|
dictAddArray( query._combined, name, elem, null ); // not dictAdd()
|
|
631
|
-
});
|
|
625
|
+
} );
|
|
632
626
|
}
|
|
633
627
|
}
|
|
634
628
|
|
|
@@ -652,10 +646,11 @@ function populate( model ) {
|
|
|
652
646
|
return null;
|
|
653
647
|
// If we allow CDL-style casts of `expand`s to associations in the future, we
|
|
654
648
|
// need to ignore an explicit type, i.e. not getOrigin():
|
|
655
|
-
const assoc = resolvePath( elem.value, '
|
|
649
|
+
const assoc = resolvePath( elem.value, 'column', elem );
|
|
656
650
|
if (!effectiveType( assoc )?.target)
|
|
657
651
|
return initFromColumns( elem, elem.expand );
|
|
658
|
-
const { targetMax } = path[path.length - 1].cardinality ||
|
|
652
|
+
const { targetMax } = path[path.length - 1].cardinality ||
|
|
653
|
+
getInheritedProp( assoc, 'cardinality' ) || {};
|
|
659
654
|
if (targetMax && (targetMax.val === '*' || targetMax.val > 1)) {
|
|
660
655
|
elem.items = { location: elem.expand[$location] };
|
|
661
656
|
setLink( elem.items, '_outer', elem );
|
|
@@ -666,18 +661,22 @@ function populate( model ) {
|
|
|
666
661
|
// TODO: make this function shorter - make part of this (e.g. setting
|
|
667
662
|
// parent/name) also be part of definer.js
|
|
668
663
|
// TODO: query is actually the elemParent, where the new elements are added to
|
|
669
|
-
// top-level:
|
|
670
|
-
// inline:
|
|
671
|
-
// expand:
|
|
664
|
+
// top-level: ( query, query.columns )
|
|
665
|
+
// inline: ( queryOrColParent, col.inline, col )
|
|
666
|
+
// expand: ( col, col.expand )
|
|
672
667
|
function initFromColumns( query, columns, inlineHead = undefined ) {
|
|
673
668
|
const elemsParent = query.items || query;
|
|
674
669
|
if (!inlineHead) {
|
|
675
|
-
elemsParent.elements = Object.create(null);
|
|
670
|
+
elemsParent.elements = Object.create( null );
|
|
676
671
|
if (query._main._leadingQuery === query) // never the case for 'expand'
|
|
677
672
|
query._main.elements = elemsParent.elements;
|
|
678
673
|
}
|
|
679
674
|
|
|
680
|
-
|
|
675
|
+
if (!columns)
|
|
676
|
+
columns = [ { val: '*' } ];
|
|
677
|
+
|
|
678
|
+
for (let i = 0; i < columns.length; ++i) {
|
|
679
|
+
const col = columns[i];
|
|
681
680
|
if (col.val === '*') {
|
|
682
681
|
const siblings = wildcardSiblings( columns, query );
|
|
683
682
|
expandWildcard( col, siblings, inlineHead, query );
|
|
@@ -689,19 +688,20 @@ function populate( model ) {
|
|
|
689
688
|
col.kind = '$inline';
|
|
690
689
|
col.name = {};
|
|
691
690
|
// a name for this internal symtab entry (e.g. '.2' to avoid clashes
|
|
692
|
-
// with real elements) is only relevant for
|
|
691
|
+
// with real elements) is only relevant for `cdsc -R`/debugging
|
|
693
692
|
const q = userQuery( query );
|
|
694
693
|
q.$inlines.push( col );
|
|
694
|
+
dependsOnSilent( q, col );
|
|
695
695
|
// or use userQuery( query ) in the following, too?
|
|
696
696
|
setMemberParent( col, `.${ q.$inlines.length }`, query );
|
|
697
697
|
initFromColumns( query, col.inline, col );
|
|
698
698
|
}
|
|
699
699
|
else if (!col.$replacement) {
|
|
700
|
-
const id = ensureColumnName( col, query );
|
|
700
|
+
const id = ensureColumnName( col, i, query );
|
|
701
701
|
col.kind = 'element';
|
|
702
702
|
dictAdd( elemsParent.elements, id, col, ( name, location ) => {
|
|
703
703
|
error( 'duplicate-definition', [ location, query ], { name, '#': 'element' } );
|
|
704
|
-
});
|
|
704
|
+
} );
|
|
705
705
|
setMemberParent( col, id, query );
|
|
706
706
|
}
|
|
707
707
|
}
|
|
@@ -710,25 +710,36 @@ function populate( model ) {
|
|
|
710
710
|
}
|
|
711
711
|
|
|
712
712
|
// TODO: probably do this already in definer.js
|
|
713
|
-
function ensureColumnName( col, query ) {
|
|
713
|
+
function ensureColumnName( col, colIndex, query ) {
|
|
714
714
|
if (col.name)
|
|
715
715
|
return col.name.id;
|
|
716
716
|
if (col.inline || col.val === '*')
|
|
717
717
|
return '';
|
|
718
718
|
const path = col.value &&
|
|
719
|
-
(col.value.path || !col.value.args && col.value.func
|
|
720
|
-
if (path
|
|
721
|
-
const last =
|
|
719
|
+
(col.value.path || !col.value.args && col.value.func?.path);
|
|
720
|
+
if (path) {
|
|
721
|
+
const last = path.length && !path.broken && path[path.length - 1];
|
|
722
722
|
if (last) {
|
|
723
723
|
col.name = { id: last.id || '', location: last.location, $inferred: 'as' };
|
|
724
724
|
return col.name.id;
|
|
725
725
|
}
|
|
726
726
|
}
|
|
727
|
-
else if (col.
|
|
728
|
-
|
|
727
|
+
else if (col.expand || col.value && (col._pathHead || query._parent.kind !== 'select')) {
|
|
728
|
+
// _pathHead => inline/expand; _parent -> only allowed in sub-selects
|
|
729
|
+
error( 'query-req-name', [ col.value?.location || col.location, query ], {},
|
|
729
730
|
'Alias name is required for this select item' );
|
|
730
731
|
}
|
|
731
|
-
|
|
732
|
+
else if (col.value) {
|
|
733
|
+
col.name = {
|
|
734
|
+
// NOTE: If the alias is changed, corresponding name-clash tests must be updated as well!
|
|
735
|
+
id: `$_column_${ colIndex + 1 }`,
|
|
736
|
+
location: col.value.location || col.location,
|
|
737
|
+
$inferred: '$internal',
|
|
738
|
+
};
|
|
739
|
+
col.$inferred = '$internal';
|
|
740
|
+
return col.name.id;
|
|
741
|
+
}
|
|
742
|
+
// invent a name for code completion in expression, see also #10596
|
|
732
743
|
col.name = {
|
|
733
744
|
id: '',
|
|
734
745
|
location: col.value && col.value.location || col.location,
|
|
@@ -763,13 +774,14 @@ function populate( model ) {
|
|
|
763
774
|
// col ($replacement set before *)
|
|
764
775
|
// false if two cols have same name
|
|
765
776
|
function wildcardSiblings( columns, query ) {
|
|
766
|
-
const siblings = Object.create(null);
|
|
777
|
+
const siblings = Object.create( null );
|
|
767
778
|
if (!columns)
|
|
768
779
|
return siblings;
|
|
769
780
|
|
|
770
781
|
let seenWildcard = null;
|
|
782
|
+
let colIndex = 0;
|
|
771
783
|
for (const col of columns) {
|
|
772
|
-
const id = ensureColumnName( col, query );
|
|
784
|
+
const id = ensureColumnName( col, colIndex, query );
|
|
773
785
|
if (id) {
|
|
774
786
|
col.$replacement = !seenWildcard;
|
|
775
787
|
siblings[id] = !(id in siblings) && col;
|
|
@@ -777,6 +789,7 @@ function populate( model ) {
|
|
|
777
789
|
else if (col.val === '*') {
|
|
778
790
|
seenWildcard = true;
|
|
779
791
|
}
|
|
792
|
+
++colIndex;
|
|
780
793
|
}
|
|
781
794
|
return siblings;
|
|
782
795
|
}
|
|
@@ -786,12 +799,15 @@ function populate( model ) {
|
|
|
786
799
|
const { elements } = query.items || query;
|
|
787
800
|
let location = wildcard.location || query.from && query.from.location || query.location;
|
|
788
801
|
const inferred = query._main.$inferred;
|
|
789
|
-
const excludingDict = (colParent || query).excludingDict || Object.create(null);
|
|
802
|
+
const excludingDict = (colParent || query).excludingDict || Object.create( null );
|
|
790
803
|
|
|
791
|
-
const envParent = wildcard._pathHead; // TODO: rename _pathHead to
|
|
804
|
+
const envParent = wildcard._pathHead; // TODO: rename _pathHead to _columnParent
|
|
792
805
|
// console.log('S1:',location.line,location.col,
|
|
793
806
|
// envParent&&!!envParent._origin&&envParent._origin.name)
|
|
794
|
-
const env =
|
|
807
|
+
const env = wildcardColumnEnv( wildcard, query );
|
|
808
|
+
if (!env)
|
|
809
|
+
return;
|
|
810
|
+
|
|
795
811
|
// if (envParent) console.log('S2:',location.line,location.col,
|
|
796
812
|
// envParent?.name,envParent?._origin?.name,
|
|
797
813
|
// Object.keys(env),Object.keys(elements))
|
|
@@ -810,15 +826,15 @@ function populate( model ) {
|
|
|
810
826
|
dictAdd( elements, name, sibling, ( _name, loc ) => {
|
|
811
827
|
// there can be a definition from a previous inline with the same name:
|
|
812
828
|
error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
|
|
813
|
-
});
|
|
829
|
+
} );
|
|
814
830
|
setMemberParent( sibling, name, query );
|
|
815
831
|
}
|
|
816
832
|
// else {
|
|
817
833
|
// sibling.$inferred = 'query';
|
|
818
834
|
// }
|
|
819
835
|
}
|
|
820
|
-
else if (Array.isArray(navElem)) {
|
|
821
|
-
const names = navElem.filter( e => !e.$duplicates)
|
|
836
|
+
else if (Array.isArray( navElem )) {
|
|
837
|
+
const names = navElem.filter( e => !e.$duplicates )
|
|
822
838
|
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
823
839
|
if (names.length) {
|
|
824
840
|
error( 'wildcard-ambiguous', [ location, query ], { id: name, names },
|
|
@@ -833,7 +849,7 @@ function populate( model ) {
|
|
|
833
849
|
dictAdd( elements, name, elem, ( _name, loc ) => {
|
|
834
850
|
// there can be a definition from a previous inline with the same name:
|
|
835
851
|
error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
|
|
836
|
-
});
|
|
852
|
+
} );
|
|
837
853
|
elem.$inferred = '*';
|
|
838
854
|
elem.name.$inferred = '*';
|
|
839
855
|
if (envParent)
|
|
@@ -851,14 +867,25 @@ function populate( model ) {
|
|
|
851
867
|
}
|
|
852
868
|
}
|
|
853
869
|
|
|
854
|
-
function
|
|
870
|
+
function wildcardColumnEnv( wildcard, query ) { // etc. wildcard._pathHead;
|
|
855
871
|
// if (envParent) console.log( 'CE:', envParent._origin, query );
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
872
|
+
const colParent = wildcard._pathHead;
|
|
873
|
+
if (!colParent)
|
|
874
|
+
return userQuery( query )._combined; // see combinedSourcesOrParentElements
|
|
875
|
+
|
|
876
|
+
const head = resolvePath( colParent.value, 'column', colParent );
|
|
877
|
+
// eslint-disable-next-line no-nested-ternary
|
|
878
|
+
if (!head
|
|
879
|
+
? !columnRefStartsWithSelf( colParent )
|
|
880
|
+
: head._main
|
|
881
|
+
? userQuery( head ) !== userQuery( query )
|
|
882
|
+
: head._main !== query._main)
|
|
883
|
+
return nestedElements( wildcard );
|
|
884
|
+
|
|
885
|
+
error( 'def-unexpected-wildcard', [ wildcard.location, colParent ], { code: '*' },
|
|
886
|
+
'Unexpected $(CODE) (wildcard) after $self/association to self reference' );
|
|
887
|
+
model.$assert = null; // explains cyclic dependencies
|
|
888
|
+
return null;
|
|
862
889
|
}
|
|
863
890
|
|
|
864
891
|
function reportReplacement( sibling, navElem, query ) {
|
|
@@ -869,7 +896,7 @@ function populate( model ) {
|
|
|
869
896
|
if (!sibling.target || sibling.target.$inferred || // not explicit REDIRECTED TO
|
|
870
897
|
path && path[path.length - 1].id !== sibling.name.id) { // or renamed
|
|
871
898
|
const { id } = sibling.name;
|
|
872
|
-
if (Array.isArray(navElem)) {
|
|
899
|
+
if (Array.isArray( navElem )) {
|
|
873
900
|
// ID published! Used in stakeholder project; if renamed, add to oldMessageIds
|
|
874
901
|
info( 'wildcard-excluding-many', [ sibling.name.location, query ],
|
|
875
902
|
{ id, keyword: 'excluding' },
|
|
@@ -902,7 +929,7 @@ function populate( model ) {
|
|
|
902
929
|
function setElementOrigin( queryElem, navElem, name, location ) {
|
|
903
930
|
const sourceElem = navElem._origin;
|
|
904
931
|
const alias = navElem._parent;
|
|
905
|
-
// always expand * to path with table alias (reason: columns
|
|
932
|
+
// always expand * to path with table alias (reason: columns $user etc)
|
|
906
933
|
const path = [ { id: alias.name.id, location }, { id: name, location } ];
|
|
907
934
|
queryElem.value = { path, location };
|
|
908
935
|
setLink( path[0], '_navigation', alias );
|
|
@@ -931,7 +958,7 @@ function populate( model ) {
|
|
|
931
958
|
// TODO: Custom kind?
|
|
932
959
|
if (elem.$isSpecifiedElement)
|
|
933
960
|
return false;
|
|
934
|
-
const assocTarget =
|
|
961
|
+
const assocTarget = assoc.target._artifact;
|
|
935
962
|
let target = assocTarget;
|
|
936
963
|
// console.log( info( null, [ elem.location, elem ], {target,art:assoc,name:''+assoc.target},
|
|
937
964
|
// 'RED').toString())
|
|
@@ -974,6 +1001,12 @@ function populate( model ) {
|
|
|
974
1001
|
|
|
975
1002
|
function redirectImplicitlyDo( elem, assoc, target, service ) {
|
|
976
1003
|
// console.log('ES:',elem.name.absolute,elem.name.element);
|
|
1004
|
+
if (assoc._main === target && elem._main?.kind === 'entity' &&
|
|
1005
|
+
elem._main?._ancestors?.includes( target )) {
|
|
1006
|
+
// source and target of the model association are the same entity, and
|
|
1007
|
+
// the current main artifact is a suitable auto-redirection target → return it
|
|
1008
|
+
return elem._main;
|
|
1009
|
+
}
|
|
977
1010
|
const elemScope = scopedRedirections && // null if no scoped redirections
|
|
978
1011
|
preferredElemScope( target, service, elem, assoc._main || assoc );
|
|
979
1012
|
const exposed = minimalExposure( target, service, elemScope );
|
|
@@ -983,7 +1016,7 @@ function populate( model ) {
|
|
|
983
1016
|
if (isAutoExposed( target ))
|
|
984
1017
|
target = createAutoExposed( origTarget, service, elemScope );
|
|
985
1018
|
const desc = origTarget._descendants ||
|
|
986
|
-
setLink( origTarget, '_descendants', Object.create(null) );
|
|
1019
|
+
setLink( origTarget, '_descendants', Object.create( null ) );
|
|
987
1020
|
if (!desc[service.name.absolute]) // could be the target itself (no repeated msgs)!
|
|
988
1021
|
desc[service.name.absolute] = [ target ];
|
|
989
1022
|
else
|
|
@@ -1005,16 +1038,16 @@ function populate( model ) {
|
|
|
1005
1038
|
std: 'Replace target $(TARGET) by one of $(SORTED_ARTS); can\'t auto-redirect this association if multiple projections exist in this service',
|
|
1006
1039
|
// eslint-disable-next-line max-len
|
|
1007
1040
|
two: 'Replace target $(TARGET) by $(SORTED_ARTS) or $(SECOND); can\'t auto-redirect this association if multiple projections exist in this service',
|
|
1008
|
-
});
|
|
1041
|
+
} );
|
|
1009
1042
|
// continuation semantics: no auto-redirection
|
|
1010
1043
|
}
|
|
1011
1044
|
else {
|
|
1012
1045
|
// referred (and probably inferred) assoc (without a user-provided target at that place)
|
|
1013
1046
|
// HINT: consider bin/cdsv2m.js when changing the following message text
|
|
1014
|
-
// No grouped and sub messages yet (TODO
|
|
1047
|
+
// No grouped and sub messages yet (TODO v5): mention at all target places with all assocs
|
|
1015
1048
|
const withAnno = annotationVal( exposed[0]['@cds.redirection.target'] );
|
|
1016
1049
|
for (const proj of exposed) {
|
|
1017
|
-
// TODO: def-ambiguous-target (just
|
|
1050
|
+
// TODO: def-ambiguous-target (just v5, as the current is infamous and used in options),
|
|
1018
1051
|
message( 'redirected-implicitly-ambiguous',
|
|
1019
1052
|
[ weakLocation( proj.name.location ), proj ],
|
|
1020
1053
|
{
|
|
@@ -1038,7 +1071,7 @@ function populate( model ) {
|
|
|
1038
1071
|
return target;
|
|
1039
1072
|
}
|
|
1040
1073
|
|
|
1041
|
-
// Return projections of `target` in `service`.
|
|
1074
|
+
// Return projections of `target` in `service`. Sorted by
|
|
1042
1075
|
// - first, only consider projections with @cds.redirection.target=true
|
|
1043
1076
|
// - exclude all indirect projections, i.e. those which are projection on others in list
|
|
1044
1077
|
//
|
|
@@ -1055,7 +1088,7 @@ function populate( model ) {
|
|
|
1055
1088
|
return exposed || [];
|
|
1056
1089
|
let min = [];
|
|
1057
1090
|
for (const e of exposed) {
|
|
1058
|
-
if (min.every( m => m._ancestors?.includes( e ))) {
|
|
1091
|
+
if (min.every( m => m._ancestors?.includes( e ) )) {
|
|
1059
1092
|
min = [ e ];
|
|
1060
1093
|
}
|
|
1061
1094
|
else if (min.length !== 1 || !e._ancestors?.includes( min[0] )) {
|
|
@@ -1139,9 +1172,10 @@ function populate( model ) {
|
|
|
1139
1172
|
function isDirectProjection( proj, base ) {
|
|
1140
1173
|
return proj.kind === 'entity' && // not event
|
|
1141
1174
|
// direct proj (TODO: or should we add them to another list?)
|
|
1175
|
+
// TODO: delete ENTITY._from
|
|
1142
1176
|
proj.query && proj.query.op && proj.query.op.val === 'SELECT' &&
|
|
1143
1177
|
proj._from && proj._from.length === 1 &&
|
|
1144
|
-
base === resolvePath( proj._from[0], 'from', proj );
|
|
1178
|
+
base === resolvePath( proj._from[0], 'from', proj.query );
|
|
1145
1179
|
}
|
|
1146
1180
|
|
|
1147
1181
|
// Auto-exposure -----------------------------------------------------------
|
|
@@ -1152,7 +1186,8 @@ function populate( model ) {
|
|
|
1152
1186
|
return target.$autoexpose;
|
|
1153
1187
|
const origTarget = target;
|
|
1154
1188
|
const chain = [];
|
|
1155
|
-
|
|
1189
|
+
const alias1 = target._from?.[0]; // TODO: delete ENTITY._from ?
|
|
1190
|
+
let source = alias1 && resolvePath( alias1, 'from', alias1._parent );
|
|
1156
1191
|
// query source ref might not have been resolved yet, cycle avoided as
|
|
1157
1192
|
// setAutoExposed() sets $autoexpose and a second call on same art would
|
|
1158
1193
|
// return false
|
|
@@ -1160,7 +1195,8 @@ function populate( model ) {
|
|
|
1160
1195
|
// stop at first ancestor with annotation or at non-query entity
|
|
1161
1196
|
chain.push( target );
|
|
1162
1197
|
target = source;
|
|
1163
|
-
|
|
1198
|
+
const alias = target._from?.[0]; // TODO: delete ENTITY._from ?
|
|
1199
|
+
source = alias && resolvePath( alias, 'from', alias._parent );
|
|
1164
1200
|
}
|
|
1165
1201
|
const autoexpose = target.$autoexpose;
|
|
1166
1202
|
if (typeof autoexpose === 'boolean') {
|
|
@@ -1199,7 +1235,7 @@ function populate( model ) {
|
|
|
1199
1235
|
return `${ service.name.absolute }.${ absolute }`;
|
|
1200
1236
|
const base = definitionScope( target );
|
|
1201
1237
|
if (base === target)
|
|
1202
|
-
return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf('.') + 1 ) }`;
|
|
1238
|
+
return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf( '.' ) + 1 ) }`;
|
|
1203
1239
|
// for scoped (e.g. calculated) entities, use exposed name of base:
|
|
1204
1240
|
const exposed = minimalExposure( base, service, elemScope );
|
|
1205
1241
|
// console.log(exposed.map( a => a.name.absolute ));
|
|
@@ -1271,18 +1307,18 @@ function populate( model ) {
|
|
|
1271
1307
|
};
|
|
1272
1308
|
// forward target parameters to projection
|
|
1273
1309
|
if (target.params) {
|
|
1274
|
-
art.params = Object.create(null);
|
|
1310
|
+
art.params = Object.create( null );
|
|
1275
1311
|
// is art.query.from.path[0].$syntax: ':' required?
|
|
1276
|
-
art.query.from.path[0].args = Object.create(null);
|
|
1277
|
-
forEachGeneric(target, 'params', (p, pn) => {
|
|
1278
|
-
art.params[pn] = linkToOrigin(p, pn, art, 'params', p.location);
|
|
1312
|
+
art.query.from.path[0].args = Object.create( null );
|
|
1313
|
+
forEachGeneric( target, 'params', (p, pn) => {
|
|
1314
|
+
art.params[pn] = linkToOrigin( p, pn, art, 'params', p.location );
|
|
1279
1315
|
art.query.from.path[0].args[pn] = {
|
|
1280
1316
|
name: { id: p.name.id, location: p.location },
|
|
1281
1317
|
location: p.location,
|
|
1282
1318
|
scope: 'param',
|
|
1283
1319
|
path: [ { id: pn, location: p.location } ],
|
|
1284
1320
|
};
|
|
1285
|
-
});
|
|
1321
|
+
} );
|
|
1286
1322
|
}
|
|
1287
1323
|
// TODO: do we need to tag the generated entity with elemScope = 'auto'?
|
|
1288
1324
|
if (autoexposed) {
|