@sap/cds-compiler 5.1.2 → 5.3.0
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 +58 -0
- package/bin/cdsc.js +7 -2
- package/bin/cdshi.js +24 -17
- package/bin/cdsse.js +17 -18
- package/doc/CHANGELOG_BETA.md +9 -4
- package/lib/api/main.js +19 -2
- package/lib/api/options.js +4 -1
- package/lib/api/validate.js +5 -0
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +40 -3
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +0 -11
- package/lib/checks/actionsFunctions.js +0 -12
- package/lib/checks/structuredAnnoExpressions.js +10 -14
- package/lib/compiler/assert-consistency.js +21 -13
- package/lib/compiler/builtins.js +2 -2
- package/lib/compiler/checks.js +25 -6
- package/lib/compiler/define.js +27 -31
- package/lib/compiler/extend.js +16 -18
- package/lib/compiler/generate.js +3 -3
- package/lib/compiler/populate.js +22 -16
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolve.js +87 -94
- package/lib/compiler/shared.js +12 -13
- package/lib/compiler/tweak-assocs.js +390 -86
- package/lib/compiler/utils.js +41 -33
- package/lib/compiler/xpr-rewrite.js +45 -58
- package/lib/edm/annotations/genericTranslation.js +17 -13
- package/lib/edm/csn2edm.js +28 -4
- package/lib/edm/edm.js +68 -28
- package/lib/edm/edmInboundChecks.js +5 -8
- package/lib/edm/edmPreprocessor.js +66 -40
- package/lib/edm/edmUtils.js +1 -1
- package/lib/gen/BaseParser.js +778 -0
- package/lib/gen/CdlParser.js +4477 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +4072 -4024
- package/lib/inspect/inspectPropagation.js +1 -1
- package/lib/json/from-csn.js +5 -3
- package/lib/json/to-csn.js +7 -10
- package/lib/language/antlrParser.js +96 -0
- package/lib/language/errorStrategy.js +1 -1
- package/lib/language/genericAntlrParser.js +32 -4
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/model/cloneCsn.js +22 -13
- package/lib/model/csnUtils.js +2 -0
- package/lib/model/revealInternalProperties.js +2 -0
- package/lib/modelCompare/utils/filter.js +70 -42
- package/lib/optionProcessor.js +16 -10
- package/lib/parsers/AstBuildingParser.js +1290 -0
- package/lib/parsers/CdlGrammar.g4 +2013 -0
- package/lib/parsers/Lexer.js +249 -0
- package/lib/render/toCdl.js +46 -45
- package/lib/render/toSql.js +5 -5
- package/lib/transform/addTenantFields.js +4 -4
- package/lib/transform/db/applyTransformations.js +54 -16
- package/lib/transform/draft/odata.js +10 -11
- package/lib/transform/effective/flattening.js +10 -14
- package/lib/transform/forRelationalDB.js +7 -6
- package/lib/transform/odata/flattening.js +42 -31
- package/lib/transform/odata/toFinalBaseType.js +7 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +2 -2
- package/share/messages/redirected-to-ambiguous.md +5 -4
- package/share/messages/redirected-to-complex.md +6 -3
package/lib/compiler/utils.js
CHANGED
|
@@ -212,9 +212,11 @@ function storeExtension( elem, name, prop, parent, block ) {
|
|
|
212
212
|
/** @type {(a: any, b: any) => boolean} */
|
|
213
213
|
const testFunctionPlaceholder = () => true;
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Return path step if the path navigates along an association whose final type
|
|
217
|
+
* satisfies function `test`; "navigates along" = last path item not considered
|
|
218
|
+
* without truthy optional argument `alsoTestLast`.
|
|
219
|
+
*/
|
|
218
220
|
function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
|
|
219
221
|
for (const item of ref.path || []) {
|
|
220
222
|
const art = item && item._artifact; // item can be null with parse error
|
|
@@ -435,7 +437,7 @@ function forEachJoinOn( query, from, callback ) {
|
|
|
435
437
|
}
|
|
436
438
|
|
|
437
439
|
function forEachExprArray( query, array, refContext, exprContext, callback ) {
|
|
438
|
-
for (const expr of array
|
|
440
|
+
for (const expr of array) {
|
|
439
441
|
if (expr)
|
|
440
442
|
callback( expr, (expr.path ? refContext : exprContext), query );
|
|
441
443
|
}
|
|
@@ -512,8 +514,10 @@ function traverseQueryExtra( main, callback ) {
|
|
|
512
514
|
}
|
|
513
515
|
}
|
|
514
516
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
+
/**
|
|
518
|
+
* Returns what was available at view._from[0] before:
|
|
519
|
+
* (think first whether to really use this function)
|
|
520
|
+
*/
|
|
517
521
|
function viewFromPrimary( view ) {
|
|
518
522
|
let query = view.$queries?.[0];
|
|
519
523
|
while (query?._origin?.kind === 'select') // sub query in from
|
|
@@ -521,24 +525,26 @@ function viewFromPrimary( view ) {
|
|
|
521
525
|
return dictFirst( query?.$tableAliases );
|
|
522
526
|
}
|
|
523
527
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
528
|
+
/**
|
|
529
|
+
* About Helper property $expand for faster the XSN-to-CSN transformation
|
|
530
|
+
* - null/undefined: artifact, member, items does not contain expanded members
|
|
531
|
+
* - 'origin': all expanded (sub) elements have no new target/on and no new annotations
|
|
532
|
+
* that value is only on elements, types, and params -> no other members
|
|
533
|
+
* when set, only on elem/art with expanded elements
|
|
534
|
+
* - 'target': all expanded (sub) elements might only have new target/on, but
|
|
535
|
+
* no individual annotations on any (sub) member
|
|
536
|
+
* when set, traverse all parents where the value has been 'origin' before
|
|
537
|
+
* - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
538
|
+
* not counting propagated ones; set up to the definition (main artifact)
|
|
539
|
+
* (only set with anno on $inferred elem), annotate “beats” target
|
|
540
|
+
* Usage according to CSN flavor:
|
|
541
|
+
* - gensrc: do not render inferred elements (including expanded elements),
|
|
542
|
+
* collect annotate statements with value 'annotate'
|
|
543
|
+
* - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
544
|
+
* has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
545
|
+
* (might sometimes render the elements unnecessarily, which is not wrong)
|
|
546
|
+
* - universal: do not render expanded sub elements if $expand = 'origin'
|
|
547
|
+
*/
|
|
542
548
|
function setExpandStatus( elem, status ) {
|
|
543
549
|
// set on element
|
|
544
550
|
while (elem._main) {
|
|
@@ -607,7 +613,7 @@ function pathStartsWithSelf( ref ) {
|
|
|
607
613
|
}
|
|
608
614
|
|
|
609
615
|
function columnRefStartsWithSelf( col ) {
|
|
610
|
-
for (; col; col = col.
|
|
616
|
+
for (; col; col = col._columnParent) {
|
|
611
617
|
const ref = col.value;
|
|
612
618
|
const head = ref && !ref.scope && ref.path?.[0];
|
|
613
619
|
if (head?._navigation?.kind === '$self')
|
|
@@ -618,12 +624,14 @@ function columnRefStartsWithSelf( col ) {
|
|
|
618
624
|
return false;
|
|
619
625
|
}
|
|
620
626
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
627
|
+
/**
|
|
628
|
+
* Remark: this function is based on an early check that no target element is
|
|
629
|
+
* covered more than once by a foreign key: then…
|
|
630
|
+
* we only need to check that all foreign key references are primary keys and
|
|
631
|
+
* that the number of foreign and primary keys are the same.
|
|
632
|
+
*/
|
|
625
633
|
function isAssocToPrimaryKeys( assoc ) {
|
|
626
|
-
let
|
|
634
|
+
let keyCount = 0;
|
|
627
635
|
const { foreignKeys } = assoc;
|
|
628
636
|
if (!foreignKeys)
|
|
629
637
|
return undefined;
|
|
@@ -634,7 +642,7 @@ function isAssocToPrimaryKeys( assoc ) {
|
|
|
634
642
|
return undefined;
|
|
635
643
|
if (!elem.key?.val)
|
|
636
644
|
return false;
|
|
637
|
-
++
|
|
645
|
+
++keyCount;
|
|
638
646
|
}
|
|
639
647
|
|
|
640
648
|
const elements = assoc.target._artifact?.elements;
|
|
@@ -642,9 +650,9 @@ function isAssocToPrimaryKeys( assoc ) {
|
|
|
642
650
|
return undefined;
|
|
643
651
|
for (const name in elements) {
|
|
644
652
|
if (elements[name].key?.val)
|
|
645
|
-
--
|
|
653
|
+
--keyCount;
|
|
646
654
|
}
|
|
647
|
-
return
|
|
655
|
+
return keyCount === 0;
|
|
648
656
|
}
|
|
649
657
|
|
|
650
658
|
// only if _effectiveType has been computed:
|
|
@@ -174,7 +174,8 @@ function xprRewriteFns( model ) {
|
|
|
174
174
|
resolvePath,
|
|
175
175
|
navigationEnv,
|
|
176
176
|
resolvePathRoot,
|
|
177
|
-
|
|
177
|
+
cachedRedirectionChain,
|
|
178
|
+
findRewriteTarget,
|
|
178
179
|
} = model.$functions;
|
|
179
180
|
|
|
180
181
|
return {
|
|
@@ -355,7 +356,7 @@ function xprRewriteFns( model ) {
|
|
|
355
356
|
// On select items, use navigation elements or table alias
|
|
356
357
|
// TODO: Expand/inline paths don't have a `_navigation` property on their last
|
|
357
358
|
// path step, yet. We need to implement expand/inline.
|
|
358
|
-
const isSimpleSelectItem = target.value?.path && target._main?.query && !target.
|
|
359
|
+
const isSimpleSelectItem = target.value?.path && target._main?.query && !target._columnParent;
|
|
359
360
|
if (isSimpleSelectItem) {
|
|
360
361
|
const isSelfPath = (expr.path[0]?._navigation?.kind === '$self');
|
|
361
362
|
if (isSelfPath) {
|
|
@@ -393,21 +394,21 @@ function xprRewriteFns( model ) {
|
|
|
393
394
|
*/
|
|
394
395
|
function rewriteGenericAnnoPath( expr, config, refCtx ) {
|
|
395
396
|
const isAbsolute = isAnnoPathAbsolute( expr );
|
|
396
|
-
const
|
|
397
|
+
const startIndex = isAbsolute ? 1 : 0;
|
|
397
398
|
|
|
398
399
|
// We get the root environment now, even though below we resolve the root item
|
|
399
400
|
// again if it was absolute (e.g. $self). We do so, because for queries, we
|
|
400
401
|
// want to respect the select item's corresponding table alias.
|
|
401
402
|
const rootEnv = getRootEnv( expr, config );
|
|
402
403
|
|
|
403
|
-
// reset artifact link; we'll set it again
|
|
404
|
+
// reset artifact link; we'll set it again if there are no errors
|
|
404
405
|
setArtifactLink( expr, null );
|
|
405
406
|
|
|
406
|
-
// Adapt root path, as it isn't rewritten in rewriteItem
|
|
407
|
-
const rootItem = expr.path[0];
|
|
408
407
|
if (isAbsolute) {
|
|
409
|
-
|
|
410
|
-
|
|
408
|
+
// Adapt absolute root path, as it isn't rewritten in rewriteItem
|
|
409
|
+
// The path-prefix was already adapted in rewriteAnnoExpr().
|
|
410
|
+
delete expr.path[0]._artifact;
|
|
411
|
+
delete expr.path[0]._navigation;
|
|
411
412
|
// TODO: What about `up_`? Shouldn't we set `_navigation` as well?
|
|
412
413
|
// TODO: Can we handle `$self` of anonymous-composition-of-aspect?
|
|
413
414
|
const root = resolvePathRoot( expr, refCtx, config.target );
|
|
@@ -415,23 +416,46 @@ function xprRewriteFns( model ) {
|
|
|
415
416
|
return reportAnnoRewriteError( expr, config );
|
|
416
417
|
}
|
|
417
418
|
|
|
419
|
+
// Store the original artifact, so that we can use it to
|
|
420
|
+
// calculate a redirection chain later on.
|
|
421
|
+
expr.path.forEach((item) => {
|
|
422
|
+
if (item._artifact)
|
|
423
|
+
setLink( item, '_originalArtifact', item._artifact );
|
|
424
|
+
});
|
|
425
|
+
|
|
418
426
|
let env = rootEnv;
|
|
419
|
-
let art =
|
|
420
|
-
|
|
427
|
+
let art = expr.path[0]._artifact;
|
|
428
|
+
|
|
429
|
+
for (let i = startIndex; i < expr.path.length; ++i) {
|
|
430
|
+
if (i > startIndex && art.target) {
|
|
431
|
+
// if the current artifact is an association, we need to respect the redirection
|
|
432
|
+
// chain from original target to new one.
|
|
433
|
+
// FIXME: Won't work with associations in projected structures.
|
|
434
|
+
const origTarget = expr.path[i - 1]?._originalArtifact?.target?._artifact;
|
|
435
|
+
const chain = cachedRedirectionChain( art, origTarget );
|
|
436
|
+
if (!chain)
|
|
437
|
+
return reportAnnoRewriteError( expr, config );
|
|
438
|
+
for (const alias of chain) {
|
|
439
|
+
art = rewriteItem( expr, config, alias, i );
|
|
440
|
+
if (!art)
|
|
441
|
+
return reportAnnoRewriteError( expr, config );
|
|
442
|
+
}
|
|
443
|
+
}
|
|
421
444
|
art = rewriteItem( expr, config, env, i );
|
|
422
445
|
if (!art)
|
|
423
446
|
return reportAnnoRewriteError( expr, config );
|
|
447
|
+
// target, items, …
|
|
424
448
|
env = navigationEnv( art, null, null, 'nav' );
|
|
425
449
|
}
|
|
426
450
|
setArtifactLink( expr, art );
|
|
427
451
|
|
|
428
|
-
if (
|
|
452
|
+
if (startIndex === 0 && expr.path[0].id.startsWith('$')) {
|
|
429
453
|
if (config.isInFilter) {
|
|
430
454
|
// In filters, we must not prepend `$self`, as that would change its meaning.
|
|
431
455
|
// We must reject it. See #11775
|
|
432
456
|
return reportAnnoRewriteError( expr, config );
|
|
433
457
|
}
|
|
434
|
-
// After rewriting, an element starts with `$` -> add root prefix
|
|
458
|
+
// After rewriting, if an element starts with `$` -> add root prefix
|
|
435
459
|
prependRootPath( config.origin, config.targetRoot, expr );
|
|
436
460
|
}
|
|
437
461
|
|
|
@@ -640,62 +664,25 @@ function xprRewriteFns( model ) {
|
|
|
640
664
|
* @returns {*|null}
|
|
641
665
|
*/
|
|
642
666
|
function rewriteItem( expr, config, env, index ) {
|
|
643
|
-
const item = expr.path[index];
|
|
644
667
|
const rewriteTarget = findRewriteTarget( expr, index, env, config.target );
|
|
645
|
-
const found = setArtifactLink(
|
|
668
|
+
const found = setArtifactLink( expr.path[index], rewriteTarget[0] );
|
|
646
669
|
if (!found)
|
|
647
670
|
return null;
|
|
648
671
|
|
|
649
|
-
if (
|
|
650
|
-
//
|
|
651
|
-
|
|
652
|
-
item.id = found.name.id;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (rewriteTarget[1] > index)
|
|
672
|
+
if (rewriteTarget[1] > index) {
|
|
673
|
+
// we keep the last segment, in case it has non-enumerable properties
|
|
674
|
+
expr.path[index] = expr.path[rewriteTarget[1]];
|
|
656
675
|
expr.path.splice(index + 1, rewriteTarget[1] - index);
|
|
657
|
-
|
|
658
|
-
return rewriteTarget[0];
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
function findRewriteTarget( expr, index, env, target ) {
|
|
662
|
-
if (env.kind === '$navElement' || env.kind === '$tableAlias') {
|
|
663
|
-
const r = firstProjectionForPath( expr.path, index, env, target );
|
|
664
|
-
return [ r.elem, r.index ];
|
|
665
676
|
}
|
|
666
677
|
|
|
667
678
|
const item = expr.path[index];
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return [ null, expr.path.length ];
|
|
673
|
-
}
|
|
674
|
-
const items = (env._leadingQuery || env)._combined?.[item.id];
|
|
675
|
-
const allNavs = !items || Array.isArray(items) ? items : [ items ];
|
|
676
|
-
|
|
677
|
-
// If the annotation target itself has a table alias, require projections of that
|
|
678
|
-
// table alias. Of course, that only works if we're talking about the same query.
|
|
679
|
-
const tableAlias = (target._main?._origin === item._artifact._main &&
|
|
680
|
-
target.value?.path[0]?._navigation?.kind === '$tableAlias')
|
|
681
|
-
? target.value.path[0]._navigation : null;
|
|
682
|
-
|
|
683
|
-
// Look at all table aliase that could project `item` and only select
|
|
684
|
-
// those that have actual projections.
|
|
685
|
-
const navs = allNavs?.filter(p => p._origin === item._artifact &&
|
|
686
|
-
(!tableAlias || tableAlias === p._parent));
|
|
687
|
-
if (!navs || navs.length === 0)
|
|
688
|
-
return [ null, expr.path.length ];
|
|
689
|
-
|
|
690
|
-
// If there are multiple navigations for the element, just use the first that matches.
|
|
691
|
-
// In case of table aliases, it's just one.
|
|
692
|
-
for (const nav of navs) {
|
|
693
|
-
const r = firstProjectionForPath( expr.path, index, nav._parent, target );
|
|
694
|
-
if (r.elem)
|
|
695
|
-
return [ r.elem, r.index ];
|
|
679
|
+
if (item.id !== found.name.id || (rewriteTarget[1] - index) !== 0) {
|
|
680
|
+
// Path was rewritten; original token text string is no longer accurate
|
|
681
|
+
config.tokenExpr.$tokenTexts = true;
|
|
682
|
+
item.id = found.name.id;
|
|
696
683
|
}
|
|
697
684
|
|
|
698
|
-
return
|
|
685
|
+
return setArtifactLink( expr.path[index], found );
|
|
699
686
|
}
|
|
700
687
|
}
|
|
701
688
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isEdmPropertyRendered,
|
|
3
|
+
const { isEdmPropertyRendered, transformAnnotationExpression } = require('../../model/csnUtils');
|
|
4
4
|
const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
|
|
5
5
|
const edmUtils = require('../edmUtils.js');
|
|
6
6
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
@@ -131,7 +131,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
131
131
|
const knownAnnos = filterKnownAnnotations(carrier);
|
|
132
132
|
knownAnnos.forEach((pn) => {
|
|
133
133
|
scopeCheck.anno = pn;
|
|
134
|
-
|
|
134
|
+
transformAnnotationExpression(carrier, pn, scopeCheck, carrier.$path);
|
|
135
135
|
});
|
|
136
136
|
});
|
|
137
137
|
}
|
|
@@ -141,7 +141,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
141
141
|
const knownAnnos = filterKnownAnnotations(obj);
|
|
142
142
|
knownAnnos.forEach((pn) => {
|
|
143
143
|
scopeCheck.anno = pn;
|
|
144
|
-
|
|
144
|
+
transformAnnotationExpression(obj, pn, scopeCheck, obj.$path);
|
|
145
145
|
});
|
|
146
146
|
};
|
|
147
147
|
if (def.$isParamEntity && def._origin) {
|
|
@@ -171,7 +171,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
171
171
|
if (innerAnnotation)
|
|
172
172
|
newAnno += `.@${innerAnnotation}`;
|
|
173
173
|
edmUtils.assignAnnotation(def, newAnno, def._origin[attr]);
|
|
174
|
-
|
|
174
|
+
transformAnnotationExpression(def._origin, attr, scopeCheck, def._origin.$path);
|
|
175
175
|
if (paramAnnoParts.length > 1)
|
|
176
176
|
delete def._origin[attr];
|
|
177
177
|
}
|
|
@@ -419,7 +419,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
419
419
|
|
|
420
420
|
knownAnnos.forEach((knownAnno) => {
|
|
421
421
|
if (knownAnno.search(/\.\$edmJson\./g) < 0) {
|
|
422
|
-
|
|
422
|
+
transformAnnotationExpression(carrier, knownAnno, {
|
|
423
423
|
ref: (elemref, prop, xpr, csnPath) => {
|
|
424
424
|
if (options.isV2() && elemref.$bparam) {
|
|
425
425
|
error('odata-anno-xpr-ref', ctx.location, {
|
|
@@ -842,8 +842,8 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
842
842
|
* */
|
|
843
843
|
let newAnno;
|
|
844
844
|
const omissions = { 'Aggregation.default': 1 };
|
|
845
|
-
const nullList = { 'Core.OperationAvailable': 1 };
|
|
846
|
-
if (annoValue
|
|
845
|
+
const nullList = { 'Core.OperationAvailable': 1, 'Core.OptionalParameter': 1 };
|
|
846
|
+
if (annoValue != null && !omissions[termName] || nullList[termName]) {
|
|
847
847
|
// termName may contain a qualifier: @UI.FieldGroup#shippingStatus
|
|
848
848
|
// -> remove qualifier from termName and set Qualifier attribute in newAnno
|
|
849
849
|
const i = termName.indexOf('#');
|
|
@@ -932,7 +932,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
932
932
|
anno: msg.anno(),
|
|
933
933
|
type: dTypeName,
|
|
934
934
|
value: `"#${enumSymbol}"`,
|
|
935
|
-
rawvalues: Object.keys(typeDef.$Allowed.Symbols).map(m =>
|
|
935
|
+
rawvalues: Object.keys(typeDef.$Allowed.Symbols).map(m => `#${m}`),
|
|
936
936
|
'#': 'enum',
|
|
937
937
|
});
|
|
938
938
|
}
|
|
@@ -989,6 +989,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
989
989
|
else {
|
|
990
990
|
const res = handleSimpleValue(cAnnoValue, dTypeName, msg);
|
|
991
991
|
if (((oTermName === 'Core.OperationAvailable' && dTypeName === 'Edm.Boolean') ||
|
|
992
|
+
(oTermName === 'Core.OptionalParameter' && dTypeName === 'Edm.String') ||
|
|
992
993
|
(oTermName === 'Validation.AllowedValues' && dTypeName === 'Edm.PrimitiveType')) &&
|
|
993
994
|
cAnnoValue === null) {
|
|
994
995
|
oTarget.append(new Edm.ValueThing(v, 'Null'));
|
|
@@ -1023,7 +1024,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1023
1024
|
anno: msg.anno(),
|
|
1024
1025
|
type: dTypeName,
|
|
1025
1026
|
value: `"#${value}"`,
|
|
1026
|
-
rawvalues: expectedType.Members.map(m =>
|
|
1027
|
+
rawvalues: expectedType.Members.map(m => `#${m}`),
|
|
1027
1028
|
'#': 'enum',
|
|
1028
1029
|
});
|
|
1029
1030
|
}
|
|
@@ -1059,7 +1060,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1059
1060
|
anno: msg.anno(),
|
|
1060
1061
|
type: dTypeName,
|
|
1061
1062
|
value: value['='] || value,
|
|
1062
|
-
rawvalues: type.Members.map(m =>
|
|
1063
|
+
rawvalues: type.Members.map(m => `#${m}`),
|
|
1063
1064
|
'#': 'enum',
|
|
1064
1065
|
});
|
|
1065
1066
|
}
|
|
@@ -1119,7 +1120,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1119
1120
|
|
|
1120
1121
|
if (isEnumType(resolvedType)) {
|
|
1121
1122
|
const type = getDictType(resolvedType);
|
|
1122
|
-
const expected = type.Members.map(m =>
|
|
1123
|
+
const expected = type.Members.map(m => `#${m}`);
|
|
1123
1124
|
message('odata-anno-value', msg.location,
|
|
1124
1125
|
{
|
|
1125
1126
|
anno: msg.anno(),
|
|
@@ -1230,7 +1231,10 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1230
1231
|
}
|
|
1231
1232
|
}
|
|
1232
1233
|
else if (value === null) {
|
|
1233
|
-
if ((resolvedType == null ||
|
|
1234
|
+
if ((resolvedType == null ||
|
|
1235
|
+
resolvedType === 'Edm.PrimitiveType' ||
|
|
1236
|
+
resolvedType === 'Edm.String') &&
|
|
1237
|
+
typeName === 'String') {
|
|
1234
1238
|
resolvedType = 'Edm.String';
|
|
1235
1239
|
}
|
|
1236
1240
|
else {
|
|
@@ -1589,7 +1593,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1589
1593
|
|
|
1590
1594
|
function filterKnownAnnotations( carrier ) {
|
|
1591
1595
|
const annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
|
|
1592
|
-
const nullWhitelist = [ '@Core.OperationAvailable' ];
|
|
1596
|
+
const nullWhitelist = [ '@Core.OperationAvailable', '@Core.OptionalParameter.DefaultValue' ];
|
|
1593
1597
|
const knownAnnosP = annoNames.filter((n) => {
|
|
1594
1598
|
const tns = whatsMyTermNamespace(n);
|
|
1595
1599
|
return tns &&
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -266,8 +266,19 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
266
266
|
});
|
|
267
267
|
}
|
|
268
268
|
});
|
|
269
|
+
if (!options.odataNoCreator) {
|
|
270
|
+
// remove unqualified @Core.Links and #CAP
|
|
271
|
+
Object.keys(serviceCsn).forEach((key) => {
|
|
272
|
+
if (key === '@Core.Links' || key.startsWith('@Core.Links.') ||
|
|
273
|
+
key === '@Core.Links#CAP' || key.startsWith('@Core.Links#CAP.'))
|
|
274
|
+
delete serviceCsn[key];
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
269
278
|
// Create annotations and distribute into Schemas, merge vocabulary cross refs into xServiceRefs
|
|
270
|
-
|
|
279
|
+
addAnnotationsAndXServiceRefs();
|
|
280
|
+
if (!options.odataNoCreator)
|
|
281
|
+
LeadSchema.prepend(waterMark());
|
|
271
282
|
|
|
272
283
|
// Finally add cross service references into the EDM and extract the targetSchemaNames
|
|
273
284
|
// for the type cross check
|
|
@@ -308,6 +319,18 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
308
319
|
|
|
309
320
|
return edm;
|
|
310
321
|
|
|
322
|
+
function waterMark() {
|
|
323
|
+
const rel = new Edm.PropertyValue(v, 'rel');
|
|
324
|
+
rel._xmlOnlyAttributes.String = 'author';
|
|
325
|
+
rel._jsonOnlyAttributes['Edm.String'] = 'author';
|
|
326
|
+
const href = new Edm.PropertyValue(v, 'href');
|
|
327
|
+
href._xmlOnlyAttributes.String = 'https://cap.cloud.sap';
|
|
328
|
+
href._jsonOnlyAttributes['Edm.String'] = 'https://cap.cloud.sap';
|
|
329
|
+
const watermark = new Edm.Annotation(v, 'Core.Links', new Edm.Collection(v, new Edm.Record(v, rel, href)));
|
|
330
|
+
// watermark._edmAttributes['Qualifier'] = 'CAP';
|
|
331
|
+
return watermark;
|
|
332
|
+
}
|
|
333
|
+
|
|
311
334
|
// Sort definitions into their schema container
|
|
312
335
|
function populateSchemas( schemas ) {
|
|
313
336
|
forEach(reqDefs.definitions, ( fqName, art ) => {
|
|
@@ -713,8 +736,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
713
736
|
attributes.IsComposable = false;
|
|
714
737
|
|
|
715
738
|
/** @type {object} */
|
|
716
|
-
const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
|
|
717
|
-
: new Edm.FunctionDefinition(v, attributes);
|
|
739
|
+
const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes, actionCsn)
|
|
740
|
+
: new Edm.FunctionDefinition(v, attributes, actionCsn);
|
|
718
741
|
|
|
719
742
|
const bpType = entityCsn ? fullQualified(entityCsn.name) : undefined;
|
|
720
743
|
/*
|
|
@@ -1099,8 +1122,9 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
1099
1122
|
}
|
|
1100
1123
|
|
|
1101
1124
|
// generate the Edm.Annotations tree and append it to the corresponding schema
|
|
1102
|
-
function
|
|
1125
|
+
function addAnnotationsAndXServiceRefs( ) {
|
|
1103
1126
|
options.getFinalTypeInfo = csnUtils.getFinalTypeInfo;
|
|
1127
|
+
|
|
1104
1128
|
const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csnUtils, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
|
|
1105
1129
|
// distribute edm:Annotations into the schemas
|
|
1106
1130
|
// Distribute each anno into Schema
|