@sap/cds-compiler 2.15.2 → 3.0.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 +66 -1590
- package/bin/cdsc.js +42 -46
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +312 -143
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +80 -24
- package/lib/base/messages.js +103 -52
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +127 -10
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +63 -12
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +16 -14
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +194 -222
- package/lib/compiler/shared.js +56 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +7 -2
- package/lib/edm/annotations/genericTranslation.js +60 -6
- package/lib/edm/annotations/preprocessAnnotations.js +10 -11
- package/lib/edm/csn2edm.js +39 -41
- package/lib/edm/edm.js +22 -15
- package/lib/edm/edmPreprocessor.js +66 -69
- package/lib/edm/edmUtils.js +12 -62
- package/lib/gen/Dictionary.json +8 -6
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20717 -22376
- package/lib/json/from-csn.js +73 -68
- package/lib/json/to-csn.js +13 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +333 -259
- package/lib/language/language.g4 +600 -645
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +2 -1
- package/lib/model/csnUtils.js +183 -285
- package/lib/model/revealInternalProperties.js +32 -9
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +64 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +334 -339
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +60 -54
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +19 -22
- package/lib/transform/forOdataNew.js +13 -15
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +11 -9
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +6 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/compiler/shared.js
CHANGED
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
setArtifactLink,
|
|
12
12
|
dependsOn,
|
|
13
13
|
pathName,
|
|
14
|
+
annotationHasEllipsis,
|
|
14
15
|
} = require('./utils');
|
|
15
16
|
|
|
16
17
|
function artifactsEnv( art ) {
|
|
@@ -512,7 +513,7 @@ function fns( model ) {
|
|
|
512
513
|
if (args.length > 0) {
|
|
513
514
|
const loc = [ args[args.length - 1].location, user ];
|
|
514
515
|
if (typeArtifact.builtin)
|
|
515
|
-
|
|
516
|
+
message( 'type-ignoring-argument', loc, { art: typeArtifact } );
|
|
516
517
|
else
|
|
517
518
|
error( 'type-unexpected-argument', loc, { '#': 'std', art: typeArtifact });
|
|
518
519
|
}
|
|
@@ -673,6 +674,9 @@ function fns( model ) {
|
|
|
673
674
|
art = item._artifact;
|
|
674
675
|
if (Array.isArray(art))
|
|
675
676
|
return false;
|
|
677
|
+
if (art.$requireElementAccess && path.length === 1)
|
|
678
|
+
// Path with only one item, but we expect an element, e.g. `$at.from`.
|
|
679
|
+
signalMissingElementAccess(art, [ item.location, user ]);
|
|
676
680
|
continue;
|
|
677
681
|
}
|
|
678
682
|
|
|
@@ -808,11 +812,30 @@ function fns( model ) {
|
|
|
808
812
|
attachAndEmitValidNames(err, ...valid.reverse());
|
|
809
813
|
}
|
|
810
814
|
|
|
815
|
+
/**
|
|
816
|
+
* Emit a 'ref-expected-element' error for magic variable references
|
|
817
|
+
* that require element accesses but don't do.
|
|
818
|
+
* For example: `$at`, but `$at.from` or `$at.to` is required.
|
|
819
|
+
*
|
|
820
|
+
* @param {object} art
|
|
821
|
+
* @param {any} location
|
|
822
|
+
*/
|
|
823
|
+
function signalMissingElementAccess(art, location) {
|
|
824
|
+
const err = message( 'ref-expected-element', location,
|
|
825
|
+
{ '#': 'magicVar', id: art.name.id } );
|
|
826
|
+
// Mapping for better valid names: from -> $at.from
|
|
827
|
+
const valid = Object.keys(art.elements || {}).reduce((prev, curr) => {
|
|
828
|
+
prev[`${ art.name.id }.${ curr }`] = true;
|
|
829
|
+
return prev;
|
|
830
|
+
}, Object.create(null));
|
|
831
|
+
attachAndEmitValidNames(err, valid);
|
|
832
|
+
}
|
|
833
|
+
|
|
811
834
|
/**
|
|
812
835
|
* Attaches a dictionary of valid names to the given compiler message.
|
|
813
836
|
* In test mode, an info message is emitted with a list of valid names.
|
|
814
837
|
*
|
|
815
|
-
* @param {
|
|
838
|
+
* @param {CompileMessage} msg CDS Compiler message
|
|
816
839
|
* @param {...object} validDicts One ore more artifact dictionaries such as in `_block`.
|
|
817
840
|
*/
|
|
818
841
|
function attachAndEmitValidNames(msg, ...validDicts) {
|
|
@@ -838,14 +861,10 @@ function fns( model ) {
|
|
|
838
861
|
}
|
|
839
862
|
}
|
|
840
863
|
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
|
|
845
|
-
// * do something for extensions by CSN or Properties parsers
|
|
846
|
-
// * make sure that we do not issue repeated warnings due to flattening if an
|
|
847
|
-
// annotation definition is missing
|
|
848
|
-
function defineAnnotations( construct, art, block, priority = 'define' ) {
|
|
864
|
+
// Set _block links for annotations (necessary for layering).
|
|
865
|
+
// Issue messages for annotations on namespaces and builtins (TODO: really here?)
|
|
866
|
+
// Also copy annotations from `construct` to `art` (TODO: separate that functionality).
|
|
867
|
+
function defineAnnotations( construct, art, block, priority = false ) {
|
|
849
868
|
if (!options.parseCdl && construct.kind === 'annotate') {
|
|
850
869
|
// Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
|
|
851
870
|
// they can still be applied. Namespace annotations are extracted in to-csn.js
|
|
@@ -862,77 +881,38 @@ function fns( model ) {
|
|
|
862
881
|
info( 'anno-builtin', [ construct.name.location, construct ], {},
|
|
863
882
|
'Builtin types should not be annotated. Use custom type instead' );
|
|
864
883
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
if (!block || block.$frontend !== 'json')
|
|
873
|
-
return; // namespace, or in CDL source without @annos:
|
|
874
|
-
// CSN input: set _block and $priority, shallow-copy from extension
|
|
875
|
-
for (const annoProp in construct) {
|
|
876
|
-
if (annoProp.charAt(0) === '@') {
|
|
877
|
-
let annos = construct[annoProp];
|
|
878
|
-
if (!(Array.isArray(annos)))
|
|
879
|
-
annos = [ annos ];
|
|
880
|
-
for (const a of annos) {
|
|
881
|
-
setLink( a, '_block', block );
|
|
882
|
-
a.$priority = priority;
|
|
883
|
-
if (construct !== art)
|
|
884
|
-
addAnnotation( art, annoProp, a );
|
|
885
|
-
}
|
|
886
|
-
}
|
|
884
|
+
else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
|
|
885
|
+
art.kind !== 'function' ) {
|
|
886
|
+
// `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
|
|
887
|
+
// for non-actions. We can't only check for !art.returns, because `action A();` is valid.
|
|
888
|
+
// `art._block` ensures that `art` is a defined def.
|
|
889
|
+
warning('anno-unexpected-returns', [ construct.name.location, construct ],
|
|
890
|
+
{ keyword: 'returns', kind: art.kind }, 'Unexpected $(KEYWORD) for $(KIND)');
|
|
887
891
|
}
|
|
888
|
-
return;
|
|
889
892
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
error( 'anno-duplicate-variant', [ item.name.variant.location, construct ],
|
|
911
|
-
{}, // TODO: params
|
|
912
|
-
'Annotation variant has been already provided' );
|
|
913
|
-
}
|
|
914
|
-
prop = `${ prop }#${ item.name.variant.id }`; // TODO: check for double variants
|
|
893
|
+
if (construct.doc)
|
|
894
|
+
art.doc = construct.doc; // e.g. through `extensions` array in CSN
|
|
895
|
+
|
|
896
|
+
// set _block (for layering) and $priority, shallow-copy from extension
|
|
897
|
+
// TODO: think of removing $priority, then
|
|
898
|
+
// no _block: define, _block: annotate/extend/edmx
|
|
899
|
+
// would fit with extending defs with props like length
|
|
900
|
+
for (const annoProp in construct) {
|
|
901
|
+
if (annoProp.charAt(0) === '@') {
|
|
902
|
+
let annos = construct[annoProp];
|
|
903
|
+
if (!(Array.isArray(annos)))
|
|
904
|
+
annos = [ annos ];
|
|
905
|
+
for (const a of annos) {
|
|
906
|
+
setLink( a, '_block', block );
|
|
907
|
+
a.$priority = priority; // is now: undefined (auto-set) | false | 'annotate' | 'extend'
|
|
908
|
+
if (construct !== art)
|
|
909
|
+
addAnnotation( art, annoProp, a );
|
|
910
|
+
if (!priority && annotationHasEllipsis( a )) {
|
|
911
|
+
error( 'anno-unexpected-ellipsis',
|
|
912
|
+
[ a.name.location, art ], { code: '...' } );
|
|
915
913
|
}
|
|
916
|
-
flatten( [ ...path, ...item.name.path ], `${ annoProp }.${ prop }`, item, iHaveVariant || item.name.variant);
|
|
917
|
-
}
|
|
918
|
-
for (const prop in value.struct) {
|
|
919
|
-
const item = value.struct[prop];
|
|
920
|
-
flatten( [ ...path, item.name ], `${ annoProp }.${ prop }`, item, iHaveVariant );
|
|
921
914
|
}
|
|
922
|
-
return;
|
|
923
915
|
}
|
|
924
|
-
const anno = Object.assign( {}, value ); // shallow copy
|
|
925
|
-
anno.name = {
|
|
926
|
-
path,
|
|
927
|
-
location: location ||
|
|
928
|
-
value.name && value.name.location ||
|
|
929
|
-
value.path && value.path.location,
|
|
930
|
-
};
|
|
931
|
-
setLink( anno, '_block', block );
|
|
932
|
-
// TODO: _parent, _main is set later (if we have ElementRef), or do we
|
|
933
|
-
// set _artifact?
|
|
934
|
-
anno.$priority = priority;
|
|
935
|
-
addAnnotation( art, annoProp, anno );
|
|
936
916
|
}
|
|
937
917
|
}
|
|
938
918
|
}
|
|
@@ -39,7 +39,7 @@ function tweakAssocs( model ) {
|
|
|
39
39
|
const { environment } = model.$volatileFunctions;
|
|
40
40
|
|
|
41
41
|
// behavior depending on option `deprecated`:
|
|
42
|
-
const enableExpandElements = !isDeprecatedEnabled( options, '
|
|
42
|
+
const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
|
|
43
43
|
// TODO: we should get rid of noElementsExpansion soon; both
|
|
44
44
|
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
45
45
|
|
|
@@ -95,15 +95,14 @@ function tweakAssocs( model ) {
|
|
|
95
95
|
if (!target || target._service) // assoc to other service is OK
|
|
96
96
|
return;
|
|
97
97
|
if (!elem.$inferred) { // && !elem.target.$inferred
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} );
|
|
98
|
+
info( 'assoc-target-not-in-service', [ elem.target.location, elem ],
|
|
99
|
+
{ target, '#': (elem._main.query ? 'select' : 'define') }, {
|
|
100
|
+
std: 'Target $(TARGET) of association is outside any service', // not used
|
|
101
|
+
// eslint-disable-next-line max-len
|
|
102
|
+
define: 'Target $(TARGET) of explicitly defined association is outside any service',
|
|
103
|
+
// eslint-disable-next-line max-len
|
|
104
|
+
select: 'Target $(TARGET) of explicitly selected association is outside any service',
|
|
105
|
+
} );
|
|
107
106
|
}
|
|
108
107
|
else {
|
|
109
108
|
info( 'assoc-outside-service', [ elem.target.location, elem ],
|
package/lib/compiler/utils.js
CHANGED
|
@@ -31,6 +31,10 @@ function annotationVal( anno ) {
|
|
|
31
31
|
function annotationIsFalse( anno ) { // falsy, but not null (unset)
|
|
32
32
|
return anno && (anno.val === false || anno.val === 0 || anno.val === '');
|
|
33
33
|
}
|
|
34
|
+
function annotationHasEllipsis( anno ) {
|
|
35
|
+
const { val } = anno || {};
|
|
36
|
+
return Array.isArray( val ) && val.some( v => v.literal === 'token' && v.val === '...' );
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
40
|
* Set compiler-calculated annotation value.
|
|
@@ -184,8 +188,8 @@ function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = fa
|
|
|
184
188
|
*
|
|
185
189
|
* @param {XSN.Path} path
|
|
186
190
|
*/
|
|
187
|
-
function pathName(path) {
|
|
188
|
-
return (path.broken) ?
|
|
191
|
+
function pathName( path ) {
|
|
192
|
+
return (path && !path.broken) ? path.map( id => id.id ).join('.') : '';
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
/**
|
|
@@ -381,6 +385,7 @@ module.exports = {
|
|
|
381
385
|
pushLink,
|
|
382
386
|
annotationVal,
|
|
383
387
|
annotationIsFalse,
|
|
388
|
+
annotationHasEllipsis,
|
|
384
389
|
annotateWith,
|
|
385
390
|
setLink,
|
|
386
391
|
setArtifactLink,
|
|
@@ -333,7 +333,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
333
333
|
function handleNestedElements(objname, baseElemName, elementsObj) {
|
|
334
334
|
if(!elementsObj) return;
|
|
335
335
|
Object.entries(elementsObj).forEach(([elemName, element]) => {
|
|
336
|
-
if (Object.keys(element).filter( x => x
|
|
336
|
+
if (Object.keys(element).filter( x => x[0] === '@' ).filter(filterKnownVocabularies).length > 0) {
|
|
337
337
|
message(warning, null, `annotations at nested elements are not yet supported, object ${objname}, element ${baseElemName}.${elemName}`);
|
|
338
338
|
}
|
|
339
339
|
|
|
@@ -437,11 +437,17 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
437
437
|
|
|
438
438
|
// Filter unknown toplevel annotations
|
|
439
439
|
// Final filtering of all annotations is done in handleTerm
|
|
440
|
-
|
|
440
|
+
|
|
441
|
+
let annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
|
|
441
442
|
const nullWhitelist = [ '@Core.OperationAvailable' ];
|
|
442
|
-
|
|
443
|
+
let knownAnnos = annoNames.filter(filterKnownVocabularies).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
|
|
443
444
|
if (knownAnnos.length === 0) return;
|
|
444
445
|
|
|
446
|
+
if(rewriteInnerAnnotations()) {
|
|
447
|
+
annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
|
|
448
|
+
knownAnnos = annoNames.filter(filterKnownVocabularies).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
|
|
449
|
+
if (knownAnnos.length === 0) return;
|
|
450
|
+
}
|
|
445
451
|
const prefixTree = createPrefixTree();
|
|
446
452
|
|
|
447
453
|
// usually, for a given carrier there is one target
|
|
@@ -610,6 +616,51 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
610
616
|
*/
|
|
611
617
|
}
|
|
612
618
|
|
|
619
|
+
function rewriteInnerAnnotations() {
|
|
620
|
+
let rc = false;
|
|
621
|
+
for (let a of knownAnnos) {
|
|
622
|
+
const [ prefix, innerAnnotation ] = a.split('.@');
|
|
623
|
+
/*
|
|
624
|
+
New inner annotation (de-)structuring of the core compiler to make
|
|
625
|
+
$value arrays extendable via ellipsis
|
|
626
|
+
@anno: { $value: [ ... ], @innerAnno: ... } is now cracked up by
|
|
627
|
+
the core compiler into:
|
|
628
|
+
@anno: [ ...]
|
|
629
|
+
@anno.@innerAnno: ...
|
|
630
|
+
|
|
631
|
+
Conflict handling if $value is present:
|
|
632
|
+
@anno
|
|
633
|
+
@anno.$value
|
|
634
|
+
@anno.@innerAnno
|
|
635
|
+
|
|
636
|
+
@anno has precedence (as it was before this change) but now
|
|
637
|
+
@anno.$value is overwritten with @anno and the inner annotations
|
|
638
|
+
are applied.
|
|
639
|
+
|
|
640
|
+
Trigger is always the inner annotation, if no inner annotation
|
|
641
|
+
is available, @anno has precedence.
|
|
642
|
+
|
|
643
|
+
Insert $value into $edmJson with inner annotation as well.
|
|
644
|
+
*/
|
|
645
|
+
if(innerAnnotation) {
|
|
646
|
+
if(carrier[prefix]) {
|
|
647
|
+
const valPrefix = prefix + '.$value';
|
|
648
|
+
carrier[valPrefix] = carrier[prefix];
|
|
649
|
+
delete carrier[prefix];
|
|
650
|
+
rc = true;
|
|
651
|
+
}
|
|
652
|
+
const edmJsonPrefix = prefix + '.$edmJson';
|
|
653
|
+
if(carrier[edmJsonPrefix]) {
|
|
654
|
+
const valPrefix = prefix + '.$value.$edmJson';
|
|
655
|
+
carrier[valPrefix] = carrier[edmJsonPrefix];
|
|
656
|
+
delete carrier[edmJsonPrefix];
|
|
657
|
+
rc = true;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return rc;
|
|
662
|
+
}
|
|
663
|
+
|
|
613
664
|
function createPrefixTree() {
|
|
614
665
|
// in csn, all annotations are flattened
|
|
615
666
|
// => values can be - primitive values (string, number)
|
|
@@ -619,6 +670,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
619
670
|
// by building a "prefix tree" for the annotations attached to the carrier
|
|
620
671
|
// see example at definition of function mergePathStepsIntoPrefixTree
|
|
621
672
|
const prefixTree = {};
|
|
673
|
+
|
|
622
674
|
for (let a of knownAnnos) {
|
|
623
675
|
// remove leading @ and split at "."
|
|
624
676
|
// stop splitting at ".@" (used for nested annotations)
|
|
@@ -845,7 +897,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
845
897
|
// "pseudo-structure" used for embedding a piece of JSON that represents "OData CSDL, JSON Representation"
|
|
846
898
|
oTarget.append(handleEdmJson(cAnnoValue['$edmJson'], context));
|
|
847
899
|
}
|
|
848
|
-
else if ( Object.keys(cAnnoValue).filter( x => x
|
|
900
|
+
else if ( Object.keys(cAnnoValue).filter( x => x[0] !== '@' ).length === 0) {
|
|
849
901
|
// object consists only of properties starting with "@"
|
|
850
902
|
message(warning, context, 'nested annotations without corresponding base annotation');
|
|
851
903
|
}
|
|
@@ -952,6 +1004,8 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
952
1004
|
// Edm.Decimal -> Decimal
|
|
953
1005
|
// integer tpye -> Int
|
|
954
1006
|
function handleSimpleValue(val, dTypeName, context) {
|
|
1007
|
+
// these types must be represented as "String" values in XML:
|
|
1008
|
+
const castToXmlString = [ 'Edm.PrimitiveType', 'Edm.Stream', 'Edm.Untyped' ];
|
|
955
1009
|
// caller already made sure that val is neither object nor array
|
|
956
1010
|
dTypeName = resolveType(dTypeName);
|
|
957
1011
|
|
|
@@ -989,12 +1043,12 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
989
1043
|
message(warning, context, `found String, but expected enum type ${ dTypeName }`);
|
|
990
1044
|
typeName = 'EnumMember';
|
|
991
1045
|
}
|
|
992
|
-
else if (dTypeName && dTypeName.startsWith('Edm.') && dTypeName
|
|
1046
|
+
else if (dTypeName && dTypeName.startsWith('Edm.') && !castToXmlString.includes(dTypeName)) {
|
|
993
1047
|
// this covers also all paths
|
|
994
1048
|
typeName = dTypeName.substring(4);
|
|
995
1049
|
}
|
|
996
1050
|
else {
|
|
997
|
-
if(dTypeName == undefined ||
|
|
1051
|
+
if(dTypeName == undefined || castToXmlString.some(t => t === dTypeName))
|
|
998
1052
|
dTypeName = 'Edm.String';
|
|
999
1053
|
// TODO
|
|
1000
1054
|
//message(warning, context, "type is not yet handled: found String, expected type: " + dTypeName);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const edmUtils = require('../edmUtils.js');
|
|
4
3
|
const { makeMessageFunction } = require('../../base/messages.js');
|
|
4
|
+
const { forEachDefinition } = require('../../model/csnUtils.js');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
/**************************************************************************************************
|
|
@@ -37,15 +37,14 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
37
37
|
let targetName = (typeof assoc.target === 'object') ? assoc.target.name : assoc.target;
|
|
38
38
|
let target = (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
|
|
39
39
|
|
|
40
|
-
let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key);
|
|
40
|
+
let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
|
|
41
41
|
if (keyNames.length === 0) {
|
|
42
42
|
keyNames.push('MISSING');
|
|
43
43
|
warning(null, null, `in annotation preprocessing: target ${targetName} has no key`);
|
|
44
44
|
}
|
|
45
45
|
else if (keyNames.length > 1)
|
|
46
46
|
warning(null, null, `in annotation preprocessing: target ${targetName} has multiple key elements`);
|
|
47
|
-
|
|
48
|
-
// TODO: what happens if key of target is itself a managed association?
|
|
47
|
+
|
|
49
48
|
return keyNames[0];
|
|
50
49
|
}
|
|
51
50
|
|
|
@@ -58,15 +57,15 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
58
57
|
function resolveShortcuts() {
|
|
59
58
|
let art = null;
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
forEachDefinition(csn, (artifact, artifactName) => {
|
|
62
61
|
if(artifactName == serviceName || artifactName.startsWith(serviceName + '.')) {
|
|
63
62
|
art = artifactName;
|
|
64
63
|
handleAnnotations(artifactName, artifact);
|
|
65
|
-
|
|
64
|
+
artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
|
|
66
65
|
handleAnnotations(elementName, element);
|
|
67
66
|
});
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
artifact.actions && Object.values(artifact.actions).forEach(action => {
|
|
68
|
+
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
70
69
|
handleAnnotations(paramName, param);
|
|
71
70
|
});
|
|
72
71
|
});
|
|
@@ -155,7 +154,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
155
154
|
return false;
|
|
156
155
|
}
|
|
157
156
|
let assoc = csn.definitions[art].elements[assocName];
|
|
158
|
-
if (!assoc || !
|
|
157
|
+
if (!assoc || !assoc.target) {
|
|
159
158
|
warning(null, null, `in annotation preprocessing/${aNameWithoutQualifier}: there is no association "${assocName}", ${ctx}`);
|
|
160
159
|
return false;
|
|
161
160
|
}
|
|
@@ -196,7 +195,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
196
195
|
// name of the element carrying the value help annotation
|
|
197
196
|
// if this is a managed assoc, use fk field instead (if there is a single one)
|
|
198
197
|
let localDataProp = carrierName.split('/').pop();
|
|
199
|
-
if (
|
|
198
|
+
if (carrier.target && carrier.on === undefined) {
|
|
200
199
|
localDataProp = localDataProp + fkSeparator + getKeyOfTargetOfManagedAssoc(carrier);
|
|
201
200
|
}
|
|
202
201
|
|
|
@@ -212,7 +211,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
212
211
|
// valueListProp: the (single) key field of the value list entity
|
|
213
212
|
// if no key or multiple keys -> warning
|
|
214
213
|
let valueListProp = null;
|
|
215
|
-
let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key );
|
|
214
|
+
let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key && !vlEntity.elements[x].target );
|
|
216
215
|
if (keys.length === 0) {
|
|
217
216
|
warning(null, null, `in annotation preprocessing/value help shortcut: entity "${enameFull}" has no key, ${ctx}`);
|
|
218
217
|
return false;
|