Socket
Socket
Sign inDemoInstall

@sap/cds-compiler

Package Overview
Dependencies
Maintainers
1
Versions
105
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/cds-compiler - npm Package Compare versions

Comparing version 4.8.0 to 4.9.0

bin/cds_remove_invalid_whitespace.js

7

bin/cds_update_identifiers.js
#!/usr/bin/env node
//
// Update CDS Delimited Identifiers

@@ -15,7 +14,7 @@ //

// Usage:
// cds_update_identifiers.js my_file.cds
// ./cds_update_identifiers.js my_file.cds
//
// If you want to update all identifiers in a directory, you can use
// this Shell script:
// find . -type f -iname '*.cds' -exec cds_update_identifiers.js {} \;
// find . -type f -iname '*.cds' -exec ./cds_update_identifiers.js {} \;
//

@@ -74,3 +73,3 @@ // Note that you need to update the path to this script in the commands above.

console.error(`Found ${errors.length} errors! \n`);
exitError('The parser emitted errors. Please fix them first and try again.');
exitError('The CDS parser emitted errors. Fix them first and try again.');
}

@@ -77,0 +76,0 @@

@@ -281,2 +281,3 @@ #!/usr/bin/env node

toEffectiveCsn,
forSeal,
};

@@ -322,3 +323,3 @@ const commandsWithoutCompilation = {

function toEffectiveCsn( model ) {
const features = [ 'resolveSimpleTypes', 'resolveProjections' ];
const features = [ 'resolveSimpleTypes', 'resolveProjections', 'remapOdataAnnotations', 'keepLocalized' ];
for (const feature of features) {

@@ -334,2 +335,14 @@ if (options[feature]) // map to boolean equivalent

function forSeal( model ) {
const features = [ 'remapOdataAnnotations' ];
for (const feature of features) {
if (options[feature]) // map to boolean equivalent
options[feature] = options[feature] === 'true';
}
const csn = options.directBackend ? model : compactModel(model, options);
displayNamedCsn(main.for.seal(csn, options), 'seal');
return model;
}
// Execute the command line option 'toCsn' and display the results.

@@ -336,0 +349,0 @@ // Return the original model (for chaining)

@@ -11,2 +11,21 @@ # ChangeLog of Beta Features for cdx compiler and backends

## Version 4.9.0 - 2024-04-25
## Removed `odataAnnotationExpressions`
It is now enabled by default.
## Removed `odataPathsInAnnotationExpressions`
It is now enabled by default.
## Removed `annotationExpressions`
It is now enabled by default.
## Added `temporalRawProjection`
Enables revocation of temporal where clause in projections of temporal entities if the
`@cds.valid { from, to }` annotations on the projection elements have falsy values.
## Version 4.8.0 - 2024-03-21

@@ -13,0 +32,0 @@

@@ -29,7 +29,4 @@ /** @module API */

const cloneCsn = lazyload('../model/cloneCsn');
const objectUtils = lazyload('../utils/objectUtils');
const { forEach, forEachKey } = require('../utils/objectUtils');
const { makeMessageFunction } = require('../base/messages');
const { sortCsnForTests } = require('../model/cloneCsn');
/**

@@ -63,3 +60,3 @@ * Return the artifact name for use for the hdbresult object

const relevant = {};
for (const name of relevantOptionNames ) {
for (const name of relevantOptionNames) {
if (options[name] !== undefined)

@@ -69,3 +66,3 @@ relevant[name] = options[name];

for (const name of optionalOptionNames ) {
for (const name of optionalOptionNames) {
if (options[name] !== undefined)

@@ -76,3 +73,3 @@ relevant[name] = options[name];

// eslint-disable-next-line sonarjs/no-empty-collection
for (const name of relevantGeneralOptions ) {
for (const name of relevantGeneralOptions) {
if (options[name] !== undefined)

@@ -149,3 +146,3 @@ relevant[name] = options[name];

let oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions, messageFunctions);
oDataCsn = sortCsnForTests(oDataCsn, internalOptions);
oDataCsn = cloneCsn.sortCsnForTests(oDataCsn, internalOptions);
messageFunctions.setModel(oDataCsn);

@@ -281,2 +278,3 @@ attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);

* @param {EffectiveCsnOptions} options Options
* @param {EffectiveCsnOptions} internalOptions Options that were already processed
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …

@@ -286,5 +284,3 @@ * @returns {CSN.Model} CSN transformed

*/
function forEffective( csn, options, messageFunctions ) {
const internalOptions = prepareOptions.for.effective(options);
internalOptions.transformation = 'effective';
function forEffectiveInternal( csn, options, internalOptions, messageFunctions ) {
messageFunctions.setOptions( internalOptions );

@@ -305,2 +301,36 @@ if (options.tenantDiscriminator) {

/**
* SEAL CSN transformation
*
* @param {CSN.Model} csn Plain input CSN
* @param {EffectiveCsnOptions} options Options
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
* @returns {CSN.Model} CSN transformed
* @private
*/
function forSeal( csn, options, messageFunctions ) {
const internalOptions = prepareOptions.for.seal(options);
internalOptions.transformation = 'effective';
return forEffectiveInternal(csn, options, internalOptions, messageFunctions);
}
/**
* Effective CSN transformation
*
* @param {CSN.Model} csn Plain input CSN
* @param {EffectiveCsnOptions} options Options
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
* @returns {CSN.Model} CSN transformed
* @private
*/
function forEffective( csn, options, messageFunctions ) {
const internalOptions = prepareOptions.for.effective(options);
internalOptions.transformation = 'effective';
// for.effective is still beta mode
if (!baseModel.isBetaEnabled(options, 'effectiveCsn'))
throw new baseError.CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
return forEffectiveInternal(csn, options, internalOptions, messageFunctions);
}
/**
* Process the given CSN into SQL.

@@ -363,3 +393,3 @@ *

forEach(flat, (key) => {
objectUtils.forEach(flat, (key) => {
const artifactNameLikeInCsn = key.replace(/\.[^/.]+$/, '');

@@ -382,3 +412,3 @@ nameMapping[artifactNameLikeInCsn] = key;

// now add the not-sorted stuff, like indices
forEach(sqlArtifactsNotToSort, (key) => {
objectUtils.forEach(sqlArtifactsNotToSort, (key) => {
sorted[remapName(key, sqlCSN, k => !k.endsWith('.hdbindex'))] = sqlArtifactsNotToSort[key];

@@ -405,3 +435,3 @@ });

forEach(dict, (key, value) => {
objectUtils.forEach(dict, (key, value) => {
const name = remapName(key, csn, filter);

@@ -554,3 +584,3 @@ result[name] = value;

const flatSqlDict = Object.values(hdbkinds).reduce((prev, curr) => {
forEach(curr, (name, value) => {
objectUtils.forEach(curr, (name, value) => {
prev[name] = value;

@@ -644,5 +674,5 @@ });

const result = [];
forEach(hdbkinds, (kind, artifacts) => {
objectUtils.forEach(hdbkinds, (kind, artifacts) => {
const suffix = `.${ kind }`;
forEach(artifacts, (name, sqlStatement) => {
objectUtils.forEach(artifacts, (name, sqlStatement) => {
if ( kind !== 'hdbindex' )

@@ -665,3 +695,3 @@ result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });

const result = [];
forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
objectUtils.forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
return result;

@@ -678,3 +708,3 @@ }

const result = [];
forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
objectUtils.forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
return result;

@@ -700,2 +730,6 @@ }

// no "isBetaEnabled", because this warning must also appear with "deprecated" flags
if (internalOptions.betaMode || internalOptions.beta?.v5preview)
messageFunctions.warning('api-deprecated-v5', null, null);
if (options.tenantDiscriminator) {

@@ -1031,6 +1065,6 @@ messageFunctions.error('api-invalid-option', null, {

const result = {};
forEach(toProcess, (fileType, artifacts) => {
objectUtils.forEach(toProcess, (fileType, artifacts) => {
if (fileType === 'messages')
return;
forEach(artifacts, (filename) => {
objectUtils.forEach(artifacts, (filename) => {
result[`${ filename }.${ fileType }`] = artifacts[filename];

@@ -1059,10 +1093,11 @@ });

for_effective: publishCsnProcessor(forEffective, 'for.effective'),
for_seal: publishCsnProcessor(forSeal, 'for.seal'),
/* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
preparedCsnToEdmx(csn, service, options) {
preparedCsnToEdmx(csn, service, options, makeMessageFunction( csn, options, 'to.edmx' ));
preparedCsnToEdmx(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edmx' ));
},
/* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
preparedCsnToEdm(csn, service, options) {
preparedCsnToEdm(csn, service, options, makeMessageFunction( csn, options, 'to.edm' ));
preparedCsnToEdm(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edm' ));
},

@@ -1181,3 +1216,3 @@ };

forEachKey(options.variableReplacements || {}, (name) => {
objectUtils.forEachKey(options.variableReplacements || {}, (name) => {
if (!name.startsWith('$') && name !== 'user' && name !== 'locale') {

@@ -1184,0 +1219,0 @@ messageFunctions.error('api-invalid-variable-replacement', null, {

@@ -55,2 +55,4 @@ 'use strict';

'resolveProjections',
'remapOdataAnnotations',
'keepLocalized',
];

@@ -218,3 +220,3 @@

const defaultOptions = {
sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true,
sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, remapOdataAnnotations: false, keepLocalized: false,
};

@@ -225,2 +227,11 @@ const processed = translateOptions(options, defaultOptions, hardOptions, null, [ 'sql-dialect-and-naming' ], 'for.effective');

},
seal: (options) => {
const hardOptions = {
sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, keepLocalized: false,
};
const defaultOptions = { remapOdataAnnotations: true };
const processed = translateOptions(options, defaultOptions, hardOptions, null, [ 'sql-dialect-and-naming' ], 'for.effective');
return Object.assign({}, processed);
},
java: options => translateOptions(options, { sqlMapping: 'plain' }, {}, undefined, undefined, 'for.java'),

@@ -227,0 +238,0 @@ },

@@ -143,6 +143,2 @@ 'use strict';

},
'sql-dialect-and-localized': (options, message) => {
if (options.fewerLocalizedViews && options.sqlDialect === 'hana' && (options.withHanaAssociations || options.withHanaAssociations === undefined))
message.error('api-invalid-combination', null, { '#': 'sql-dialect-and-localized', option: 'fewerLocalizedViews', value: 'hana' });
},
'beta-no-test': (options, message) => {

@@ -154,3 +150,3 @@ if (options.beta && !options.testMode)

const alwaysRunValidators = [ 'beta-no-test', 'sql-dialect-and-localized' ];
const alwaysRunValidators = [ 'beta-no-test' ];

@@ -157,0 +153,0 @@ /**

@@ -8,2 +8,28 @@ 'use strict';

/**
* Map of propagation rules for annotations.
* Does not include rules for standard propagation of annotations or other properties.
*
* @type {Record<string, string>}
*/
const propagationRules = {
__proto__: null,
'@Analytics.hidden': 'never',
'@Analytics.visible': 'never',
'@cds.autoexpose': 'onlyViaArtifact',
'@cds.autoexposed': 'never',
'@cds.external': 'never',
'@cds.persistence.calcview': 'never',
'@cds.persistence.exists': 'never',
'@cds.persistence.skip': 'notWithPersistenceTable',
'@cds.persistence.table': 'never',
'@cds.persistence.udf': 'never',
'@cds.redirection.target': 'never',
'@com.sap.gtt.core.CoreModel.Indexable': 'never',
'@fiori.draft.enabled': 'onlyViaArtifact',
'@sql.append': 'never',
'@sql.prepend': 'never',
'@sql.replace': 'never',
}
/**
* Checks whether the given absolute path is inside a reserved namespace.

@@ -84,2 +110,3 @@ *

module.exports = {
propagationRules,
xprInAnnoProperties,

@@ -86,0 +113,0 @@ functionsWithoutParens,

@@ -29,5 +29,2 @@ // module- and csn/XSN-independent definitions

// enabled by --beta-mode
annotationExpressions: true,
odataPathsInAnnotationExpressions: true,
odataAnnotationExpressions: true,
hanaAssocRealCardinality: true,

@@ -43,4 +40,6 @@ mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it

v5preview: true,
temporalRawProjection: true,
// disabled by --beta-mode
nestedServices: false,
rewriteAnnotationExpressionsViaType: false,
};

@@ -86,3 +85,3 @@

*
* Please do not move this function to the "option processor" code.
* Do not move this function to the "option processor" code.
*

@@ -107,3 +106,3 @@ * @param {object} options Options

*
* Please do not move this function to the "option processor" code.
* Do not move this function to the "option processor" code.
*

@@ -110,0 +109,0 @@ * @param {object} options Options

@@ -56,4 +56,4 @@ 'use strict';

if (type === '$self' && !this.csn.definitions.$self && i > 0) {
this.error(null, currPath.concat(pn),
'Binding parameter is expected to appear on first position only');
this.error('def-invalid-param', currPath.concat(pn),
'Binding parameter is expected to appear in first position only');
}

@@ -60,0 +60,0 @@ });

@@ -40,2 +40,3 @@ 'use strict';

this.info(null, member.$path, {},
// eslint-disable-next-line cds-compiler/message-no-quotes
'Annotation “@Analytics.Measure” expects “@Aggregation.default” to be assigned for the same element as well');

@@ -67,2 +68,3 @@ }

if (artifact.kind === 'entity' && artifact['@readonly'] && artifact['@insertonly'])
// eslint-disable-next-line cds-compiler/message-no-quotes
this.warning(null, artifact.$path, {}, 'Annotations “@readonly” and “@insertonly” can\'t be assigned in combination');

@@ -98,2 +100,3 @@ }

if (valid.key.length && !(valid.from.length && valid.to.length))
// eslint-disable-next-line cds-compiler/message-no-quotes
this.error(null, [ 'definitions', artifactName ], 'Annotation “@cds.valid.key” was used but “@cds.valid.from” and “@cds.valid.to” are missing');

@@ -100,0 +103,0 @@

@@ -26,2 +26,3 @@ 'use strict';

if (i > 1)
// eslint-disable-next-line cds-compiler/message-no-quotes
this.error(null, path, {}, 'Illegal number of unary ‘+’/‘-’ operators');

@@ -41,4 +42,6 @@ }

function rejectParamDefaultsInHanaCds( member, memberName, prop, path ) {
if (member.default && prop === 'params' && this.options.transformation === 'hdbcds')
this.error(null, path, {}, 'Parameter default values are not supported in SAP HANA CDS');
if (member.default && prop === 'params' && this.options.transformation === 'hdbcds') {
this.error('def-unsupported-param', path, {},
'Parameter default values are not supported in SAP HANA CDS');
}
}

@@ -45,0 +48,0 @@

@@ -170,2 +170,3 @@ 'use strict';

'#': cdsPersistenceSkipped ? 'std' : 'abstract',
anno: '@cds.persistence.skip',
id: nonPersistedTarget.pathStep,

@@ -175,4 +176,4 @@ elemref: { ref },

}, {
std: 'Unexpected “@cds.persistence.skip” annotation on association target $(NAME) of $(ID) in path $(ELEMREF)',
abstract: 'Unexpected “abstract” association target $(NAME) of $(ID) in path $(ELEMREF)',
std: 'Unexpected $(ANNO) annotation on association target $(NAME) of $(ID) in path $(ELEMREF)',
abstract: 'Unexpected abstract association target $(NAME) of $(ID) in path $(ELEMREF)',
} );

@@ -179,0 +180,0 @@ break; // only one error per path

@@ -6,3 +6,3 @@ 'use strict';

forEachMember, getNormalizedQuery, hasAnnotationValue,
applyTransformations, functionList,
applyTransformations, functionList, mergeTransformers,
} = require('../model/csnUtils');

@@ -160,3 +160,3 @@ const enrichCsn = require('./enricher');

applyTransformations(csn, mergeCsnValidators(csnValidators, that), [], { drillRef: true });
applyTransformations(csn, mergeTransformers(csnValidators, that), [], { drillRef: true });

@@ -186,34 +186,2 @@ forEachDefinition(csn, (artifact, artifactName, prop, path) => {

/**
* Ensure the CSN validators adhere to the applyTransformation format - also, supply correct this value for each subfunction
*
* @param {object[]} csnValidators Validators
* @param {object} that Value for this
* @returns {object} Remapped validators.
*/
function mergeCsnValidators( csnValidators, that ) {
const remapped = {};
for (const validator of csnValidators) {
for (const [ n, fns ] of Object.entries(validator)) {
if (!remapped[n])
remapped[n] = [];
if (Array.isArray(fns)) {
remapped[n].push((parent, name, prop, path) => fns.forEach(
fn => fn.bind(that)(parent, name, prop, path)
));
}
else {
remapped[n].push((parent, name, prop, path) => fns.bind(that)(parent, name, prop, path));
}
}
}
for (const [ n, fns ] of Object.entries(remapped))
remapped[n] = (parent, name, prop, path) => fns.forEach(fn => fn.bind(that)(parent, name, prop, path));
return remapped;
}
/**
* Depending on the dialect we need to run different validations.

@@ -220,0 +188,0 @@ *

@@ -132,2 +132,3 @@ // Consistency checker on model (XSN = augmented CSN)

},
tokenIndex: { test: isNumber },
location: {

@@ -140,3 +141,6 @@ // every thing with a $location in CSN must have a XSN location even

requires: [ 'file' ], // line is optional in top-level location
optional: [ 'line', 'col', 'endLine', 'endCol', '$notFound' ],
optional: [
'line', 'col', 'endLine', 'endCol', '$notFound',
'tokenIndex', // in parser for $lsp
],
schema: {

@@ -237,3 +241,3 @@ line: { test: isNumber },

requires: [ 'location' ],
optional: [ 'path' ],
optional: [ 'kind', 'name' ],
},

@@ -521,2 +525,3 @@ usings: {

'args', 'op', 'func', 'suffix',
'$invalidPaths',
],

@@ -527,2 +532,3 @@ // TODO: name requires if not in parser?

$annotations: { parser: true, kind: true, test: TODO }, // deprecated, still there for cds-lsp
$invalidPaths: { test: isBoolean },
name: {

@@ -529,0 +535,0 @@ isRequired: stageParser && (() => false), // not required in parser

@@ -22,2 +22,3 @@ // Checks on XSN performed during compile() that are useful for the user

const { typeParameters } = require('./builtins');
const { propagationRules } = require('../base/builtins');

@@ -73,2 +74,9 @@ const $location = Symbol.for( 'cds.$location' );

function* iterateAnnotations( art ) {
for (const prop in art) {
if (prop.charAt(0) === '@')
yield prop;
}
}
function checkGenericConstruct( art ) {

@@ -81,7 +89,5 @@ checkName( art );

if (model.vocabularies) {
Object.keys( art )
.filter( a => a.startsWith( '@' ) )
.forEach( a => checkAnnotationAssignment1( art, art[a] ) );
}
for (const anno of iterateAnnotations( art ))
checkAnnotationAssignment1( art, art[anno] );
checkTypeStructure( art );

@@ -242,2 +248,4 @@ checkAssociation( art ); // type def could be assoc

const type = isCast ? xpr.type : user.type;
if (!isCast && type.$inferred)
return; // e.g. $inferred:'generated'
if (elem && type) { // has explicit type

@@ -271,5 +279,7 @@ if (type._artifact?.elements)

if (elem.key?.val && elem._main?.query) {
// either the element was casted to localized (no `_origin`) or
// original element is localized but not key, as that would have
// already resulted in a warning by localized.js
if (elem._origin?.localized?.val && !elem._origin.key?.val) {
if ((!elem._origin && elem.localized?.val) ||
(elem._origin?.localized?.val && !elem._origin.key?.val)) {
warning( 'def-ignoring-localized', [ elem.key.location, elem ], { keyword: 'localized' },

@@ -868,17 +878,33 @@ 'Keyword $(KEYWORD) is ignored for primary keys' );

/**
* Returns true if the given annotation accepts expressions as values.
*
* @param {object} anno
* @param {XSN.Artifact} art
* @returns {boolean}
*/
function checkAnnotationAcceptsExpressions( anno, art ) {
const name = anno.name?.id;
if (!name)
return true;
if (!propagationRules[`@${ name }`])
return true;
error( 'anno-unexpected-expr', [ anno.location, art ], { anno: name },
'Unexpected expression as value for $(ANNO)' );
return false;
}
// Former checkAnnotationAssignments.js ------------------------------------
function checkAnnotationAssignment1( art, anno ) {
if (art.$contains?.$annotation && anno.kind === '$annotation') {
if (checkAnnotationAcceptsExpressions( anno, art ))
checkAnnotationExpressions( anno, art );
}
// Check the annotation assignments (if any) of 'annotatable', possibly using annotation
// definitions from 'model'. Report errors on 'options.messages.
//
// TODO: rework completely!
// TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
if (!model.vocabularies)
return;
// Has been slightly adapted for model.vocabularies but comments need to be
// adapted, etc.
function checkAnnotationAssignment1( art, anno ) {
if (art.$contains?.$annotation)
checkAnnotationExpressions( anno, art );
// Has been slightly adapted for model.vocabularies but comments need to be
// adapted, etc.
// TODO: rework completely!
// TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
// Sanity checks (ignore broken assignments)

@@ -885,0 +911,0 @@ if (!anno.name?.id)

@@ -216,2 +216,8 @@ // Compiler phase 1 = "define": transform dictionary of AST-like XSNs into XSN

addI18nBlocks();
const { $self } = model.definitions;
if ($self) {
message( 'name-deprecated-$self', [ $self.location, $self ], { name: '$self' },
'Do not use $(NAME) as name for an artifact definition' );
}
}

@@ -232,3 +238,3 @@

let { namespace } = src;
let namespace = src.namespace?.name;
let prefix = '';

@@ -240,3 +246,4 @@ if (namespace?.path && !namespace.path.broken) {

if (isInReservedNamespace( prefix )) {
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
{ name: 'cds' },
'The namespace $(NAME) is reserved for CDS builtins' );

@@ -529,3 +536,3 @@ namespace = null;

if (src.namespace) {
const decl = src.namespace;
const decl = src.namespace.name;
if (!decl.id) // parsing may have failed

@@ -569,4 +576,4 @@ return;

checkRedefinition( art );
initDollarSelf( art ); // $self
initMembers( art, art, block );
initDollarSelf( art ); // $self
if (art.params)

@@ -644,2 +651,3 @@ initDollarParameters( art ); // $parameters

location: art.location,
deprecated: true, // hide in code completion
};

@@ -688,4 +696,10 @@ setLink( parameters, '_parent', art );

setLink( self, '_main', query._main );
const projection = { ...self, deprecated: true }; // hide in code completion
setLink( projection, '_origin', query );
setLink( projection, '_parent', query );
setLink( projection, '_main', query._main );
query.$tableAliases.$self = self;
query.$tableAliases.$projection = self;
query.$tableAliases.$projection = projection;
}

@@ -868,8 +882,9 @@ initSubQuery( query ); // check for SELECT clauses after from / mixin

function initMixins( query, art ) {
// TODO: re-check if mixins have already duplicates
for (const name in query.mixin) {
const mixin = query.mixin[name];
forEachInOrder( query, 'mixin', initMixin );
function initMixin( mixin, name ) {
setLink( mixin, '_block', art._block );
setMemberParent( mixin, name, query );
checkRedefinition( mixin );
if (!(mixin.$duplicates)) {
setMemberParent( mixin, name, query );
setLink( mixin, '_block', art._block );
// TODO: do some initMembers() ? If people had annotation

@@ -935,10 +950,10 @@ // assignments on the mixin... (also for future mixin definitions

if (col.doc) {
warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
message( 'syntax-ignoring-anno', [ col.doc.location, col ],
{ '#': 'doc', code: '.{ ‹inline› }' } );
}
// col.$annotations no available for CSN input, have to search.
// Warning about first annotation should be enough to avoid spam.
// Message about first annotation should be enough to avoid spam.
const firstAnno = Object.keys( col ).find( key => key.startsWith( '@' ) );
if (firstAnno) {
warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
message( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
{ code: '.{ ‹inline› }' } );

@@ -1163,3 +1178,3 @@ }

if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
initBoundSelfParam( elem.params );
initBoundSelfParam( elem.params, elem._main );

@@ -1190,3 +1205,3 @@ // for a correct home path, setMemberParent needed to be called

function initBoundSelfParam( params ) {
function initBoundSelfParam( params, main ) {
if (!params)

@@ -1197,10 +1212,6 @@ return;

if (def) {
// TODO v5: bring this always, probably even as error
warning( 'name-deprecated-for-artifacts', [ def.location, def ], { name: '$self' },
'Do not use $(NAME) as name for an artifact definition' );
boundSelfParamType = false;
return;
}
const location = { file: '' };
boundSelfParamType = { name: { id: '$self', location }, location };
boundSelfParamType = '$self';
}

@@ -1211,4 +1222,5 @@ const first = params[Object.keys( params )[0] || ''];

if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
setLink( type, '_artifact', boundSelfParamType );
setLink( path[0], '_artifact', boundSelfParamType );
const { $self } = main.$tableAliases;
setLink( type, '_artifact', $self );
setLink( path[0], '_artifact', $self );
}

@@ -1215,0 +1227,0 @@ }

@@ -12,2 +12,3 @@ // Extend

isDeprecatedEnabled,
isBetaEnabled,
} = require('../base/model');

@@ -75,2 +76,3 @@ const { dictAdd, pushToDict } = require('../base/dictionaries');

const includesNonShadowedFirst = isDeprecatedEnabled( model.options, 'includesNonShadowedFirst' );
const isV5preview = isBetaEnabled( model.options, 'v5preview' );

@@ -227,2 +229,3 @@ sortModelSources();

};
// TODO(v5): Discuss: make this an error?
warning( 'ext-invalid-kind', [ loc, ext ], msgArgs, {

@@ -824,3 +827,3 @@ std: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) instead',

}
if (extensions.length === 1) { // i.e. no proper location if from more than one extensions
if (extensions.length === 1) { // i.e. no proper location if from more than one extension
annotate.location = extensions[0].location;

@@ -836,14 +839,34 @@ annotate.name.location = extensions[0].name.location;

function checkRemainingMainExtensions( art, ext, localized ) {
if (localized) // TODO v5: ignore only for annotate
if (localized) {
if (isV5preview && ext.kind === 'extend') {
// In v5, reject any `extend` on localized.
error( 'ref-undefined-art', [ ext.location || ext.name.location, ext ],
{ '#': 'localized', keyword: 'annotate' } );
}
return;
}
if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
return;
if (art?.kind === 'namespace') {
// TODO: not at all different to having no definition
info( 'anno-namespace', [ ext.name.location, ext ], {}, // TODO: better location?
'Namespaces can\'t be annotated' );
if (art?.builtin) {
info( 'anno-builtin', [ ext.name.location, ext ], {} ); // TODO: better location?
}
else if (art?.builtin) {
info( 'anno-builtin', [ ext.name.location, ext ], {}, // TODO: better location?
'Builtin types should not be annotated. Use custom type instead' );
else if (art?.kind === 'namespace') {
const hasAnnotations = Object.keys(ext).find(a => a.charAt(0) === '@');
const firstAnno = ext[hasAnnotations];
// In v5, extending namespaces is only allowed for `extend with definitions`.
// Neither annotations nor other extensions are allowed.
// Non-artifact extensions are reported in resolvePath() already (for v5).
if ((hasAnnotations || !ext.artifacts) ) {
if (isV5preview) {
error( 'ref-undefined-art', [ (firstAnno?.name || ext.name).location, ext ], {
'#': 'namespace', art: ext,
} );
}
else {
info( 'anno-namespace', [ (firstAnno?.name || ext.name).location, ext ], {},
'Namespaces can\'t be annotated nor extended' );
}
}
}

@@ -850,0 +873,0 @@ }

@@ -59,3 +59,2 @@ // Main XSN-based compiler functions

this.errors = errs;
this.hasBeenReported = false;
}

@@ -62,0 +61,0 @@ }

@@ -23,4 +23,7 @@ // Propagate properties in XSN

viewFromPrimary,
copyExpr,
} = require('./utils');
const { propagationRules } = require('../base/builtins');
const $inferred = Symbol.for( 'cds.$inferred' );
const { xprRewriteFns } = require('./xpr-rewrite');
// const { ref } = require( '../model/revealInternalProperties' )

@@ -31,19 +34,2 @@

const props = {
'@com.sap.gtt.core.CoreModel.Indexable': never,
'@cds.persistence.exists': never, // also copied in generate.js
'@cds.persistence.table': never,
'@cds.persistence.calcview': never,
'@cds.persistence.udf': never,
'@cds.persistence.skip': notWithPersistenceTable, // also copied in generate.js
// '@cds.tenant.independent' is propagated as normal, but also copied in generate.js
'@sql.append': never,
'@sql.prepend': never,
'@sql.replace': never,
'@Analytics.hidden': never,
'@Analytics.visible': never,
'@cds.autoexpose': onlyViaArtifact,
'@cds.autoexposed': never, // in case people set it themselves
'@cds.external': never,
'@cds.redirection.target': never,
'@fiori.draft.enabled': onlyViaArtifact,
'@': annotation, // always except in 'items' (and parameters for entity return types)

@@ -77,3 +63,13 @@ doc: annotation, // always except in 'items' (and parameters for entity return types)

};
const ruleToFunction = {
__proto__: null,
never,
onlyViaArtifact,
notWithPersistenceTable,
};
for (const rule in propagationRules)
props[rule] = ruleToFunction[propagationRules[rule]];
const { options } = model;
const { rewriteAnnotationsRefs } = xprRewriteFns( model );
// eslint-disable-next-line max-len

@@ -109,3 +105,3 @@ const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );

const origin = getOrigin( target );
if (origin) {
if (origin && origin.kind !== '$self') {
// Calculated elements that are simple references: `calc = field;`.

@@ -209,2 +205,6 @@ // Respect sibling properties in inheritance.

}
else if (prop.charAt(0) === '@' && val?.kind === '$annotation') {
target[prop] = Object.assign( copyExpr( val ), { $inferred: 'prop' } );
rewriteAnnotationsRefs( target, source, prop );
}
else {

@@ -278,2 +278,3 @@ target[prop] = Object.assign( {}, val, { $inferred: 'prop' } );

// Only propagate if parent object (which is not necessarily `_parent`) was propagated.
function onlyViaParent( prop, target, source ) {

@@ -288,2 +289,4 @@ if (target.$inferred === 'proxy' || target.$inferred === 'expanded')

return;
if (target.type?._artifact === model.definitions['cds.Association'])
return; // don't propagate targetAspect to associations (e.g. via $filtered)
const ta = source.targetAspect;

@@ -290,0 +293,0 @@ if (!ta.elements && !ta._origin) { // _origin set for elements in source

@@ -7,3 +7,2 @@ // Tweak associations: rewrite keys and on conditions

forEachGeneric,
forEachMember,
forEachInOrder,

@@ -37,3 +36,2 @@ } = require('../base/model');

const {
resolvePath,
traverseExpr,

@@ -83,4 +81,2 @@ checkExpr,

} );
checkForAnnotationRefs( art );
}

@@ -130,86 +126,2 @@

function checkForAnnotationRefs( art ) {
// TODO: no check for type inheritance yet
const origin = art._origin ||
art.includes && art.includes[art.includes.length - 1]._artifact;
// Make sure not to waste time if no inherited annotation has checked refs:
if (!origin?.$contains?.$annotation)
return;
for (const prop in origin) {
const anno = prop.charAt(0) === '@' && !art[prop] && origin[prop];
// Remark: to be on the academic safe side, we should consider the
// annotations which are not propagated, but they never have references as
// value. So “no” for the moment. We also do not perform these checks in
// propagator.js, as it should go away in compiler v6.
if (anno.kind) { // i.e. with values refs
art[prop] = { ...anno, $inferred: 'prop' };
setLink( art[prop], '_outer', art );
const errorRef = checkAnnotationForRefs( art[prop], art );
if (errorRef) {
const valid = errorRef.path[errorRef.path.length - 1]._artifact;
error( 'anno-missing-rewrite', [ weakLocation( art.location ), art ], {
'#': (valid ? 'unrelated' : 'std'),
anno: prop,
art: origin,
elemref: errorRef,
} );
}
art.$contains ??= {};
art.$contains.$annotation = true;
}
}
forEachMember( art, checkForAnnotationRefs );
}
function checkAnnotationForRefs( expr, user ) {
if (expr.$tokenTexts)
return traverseExpr( expr, 'annoRewrite', user, checkAnnotationRef );
if (expr.literal === 'array') {
for (const val of expr.val) {
const found = checkAnnotationForRefs( val, user );
if (found)
return found;
}
return null;
}
if (expr.literal !== 'struct')
return null;
const struct = Object.values( expr.struct );
for (const val of struct) {
const found = checkAnnotationForRefs( val, user );
if (found)
return found;
}
return null;
}
function checkAnnotationRef( ref, refCtx, user ) {
const origPath = ref.path;
if (!origPath[origPath.length - 1]._artifact) // already wrong in original
return null;
ref = { ...ref, path: [ ...origPath.map( item => ({ ...item }) ) ] };
if (!resolvePath( ref, refCtx, user ))
return ref;
return ref.path.some( isUnrelated ) && ref;
function isUnrelated( item, idx ) {
let elem = item._artifact;
const orig = origPath[idx]._artifact;
// With includes, we allow shadowing: an included element might seem to be
// unrelated.
if (elem._main?.includes && elem._main === (user._main || user))
return false;
if (!elem._effectiveType) // safety
return false;
// With redirections, the originally referred object might not be the same
// or even the direct _origin
do {
if (elem === orig)
return false;
elem = elem._origin;
} while (elem);
return true;
}
}
function rewriteAssociationCheck( element ) {

@@ -531,2 +443,14 @@ const elem = element.items || element; // TODO v5: nested items

// Published paths with filters are always associations, never
// compositions, hence we need to change the type to avoid type propagation.
const assocType = { id: 'cds.Association', location };
setArtifactLink( assocType, model.definitions['cds.Association'] );
elem.type = {
path: [ assocType ],
scope: 'global',
location,
$inferred: '$generated',
};
setArtifactLink( elem.type, assocType._artifact );
elem.$filtered = {

@@ -828,2 +752,3 @@ val: true,

item.id = name;
// TODO: Why not break here? Test test3/scenarios/AFC/db/view/consumption/C_ScopedRole.cds
}

@@ -830,0 +755,0 @@ }

@@ -22,7 +22,7 @@ 'use strict';

if (!attributes || typeof attributes !== 'object')
error(null, 'Please debug me: attributes must be a dictionary');
error(null, 'Debug me: attributes must be a dictionary');
if (!Array.isArray(version))
error(null, `Please debug me: v is either undefined or not an array: ${version}`);
error(null, `Debug me: v is either undefined or not an array: ${version}`);
if (version.filter(v => v).length !== 1)
error(null, 'Please debug me: exactly one version must be set');
error(null, 'Debug me: exactly one version must be set');

@@ -1112,3 +1112,3 @@ // Common attributes of JSON and XML.

default:
error(null, `Please debug me: Unhandled NavProp child: ${c.kind}`);
error(null, `Debug me: Unhandled NavProp child: ${c.kind}`);
}

@@ -1115,0 +1115,0 @@ });

@@ -6,2 +6,3 @@ 'use strict';

forEachDefinition, forEachMemberRecursively, getUtils,
transformExpression, findAnnotationExpression,
} = require('../model/csnUtils');

@@ -59,4 +60,6 @@ const { isBuiltinType } = require('../base/builtins');

checkIfItemsOfItems(action, undefined, undefined, aLoc);
markBindingParamPaths(action, aLoc);
forEachMemberRecursively(action, checkIfItemsOfItems, aLoc);
checkIfItemsOfItems(action.returns, undefined, undefined, aLoc.concat('returns'));
markBindingParamPaths(action, aLoc);
});

@@ -129,2 +132,32 @@ }

}
// we need to know if the first path step is the bindind param
// for the rejection of V2 paths where the BP is ignored
function markBindingParamPaths( action, loc ) {
const special$self = !csn?.definitions?.$self && '$self';
if (action.params) {
const params = Object.entries(action.params);
const firstParam = params[0][1];
const type = firstParam?.items?.type || firstParam?.type;
if (type === special$self) {
const bindingParamName = params[0][0];
const markBindingParam = {
ref: (parent, prop, xpr) => {
if ((xpr[0].id || xpr[0]) === bindingParamName)
parent.$bparam = true;
},
};
let exprAnnos = Object.keys(action).filter(pn => findAnnotationExpression(action, pn));
exprAnnos.forEach((pn) => {
transformExpression(action, pn, markBindingParam, loc);
});
forEachMemberRecursively(action, (member, _memberName, _prop, path, _parent) => {
exprAnnos = Object.keys(member).filter(pn => findAnnotationExpression(member, pn));
exprAnnos.forEach((pn) => {
transformExpression(member, pn, markBindingParam, path);
});
}, loc);
}
}
}
}

@@ -131,0 +164,0 @@

@@ -235,11 +235,9 @@ // Transform XSN (augmented CSN) into CSN

}
if (!isBetaEnabled(options, 'v5preview')) {
// 'namespace' for complete model is 'namespace' of first source
// (not a really useful property at all, avoids XSN inspection by Umbrella)
for (const first in srcDict) {
const { namespace } = srcDict[first];
if (namespace?.path)
csn.namespace = pathName( namespace.path );
break;
}
// 'namespace' for complete model is 'namespace' of first source
// (not a really useful property at all, avoids XSN inspection by Umbrella)
for (const first in srcDict) {
const { namespace } = srcDict[first];
if (namespace?.name?.path)
csn.namespace = pathName( namespace.name.path );
break;
}

@@ -1014,4 +1012,5 @@ set( 'definitions', csn, model );

// Shortcut for many cases:
if (terse && node._artifact && !node._artifact._main && terse !== '.path')
return node._artifact.name.id;
const art = node._artifact;
if (art && (!art._main || art.kind === '$self') && terse && terse !== '.path')
return art.name.id;
let { path } = node;

@@ -1018,0 +1017,0 @@ if (!path)

@@ -727,7 +727,7 @@ // Generic ANTLR parser class with AST-building functions

this.warning( 'syntax-deprecated-auto-as', ast.location, { keyword: 'as' },
'Please add the keyword $(KEYWORD) in front of the alias name' );
'Add keyword $(KEYWORD) in front of the alias name' );
}
else { // configurable error
this.message( 'syntax-missing-as', ast.location, { keyword: 'as' },
'Please add the keyword $(KEYWORD) in front of the alias name' );
'Add keyword $(KEYWORD) in front of the alias name' );
}

@@ -763,6 +763,12 @@ return ast;

// we might complain about that already here via @arg{category}
return { id, $delimited: true, location: this.tokenLocation( token ) };
const ast = { id, $delimited: true, location: this.tokenLocation( token ) };
ast.location.tokenIndex = token.tokenIndex;
return ast;
}
if (token.text[0] !== '"')
return { id, location: this.tokenLocation( token ) };
if (token.text[0] !== '"') {
const ast = { id, location: this.tokenLocation(token) };
ast.location.tokenIndex = token.tokenIndex;
return ast;
}
// delimited:

@@ -778,3 +784,5 @@ id = id.slice( 1, -1 ).replace( /""/g, '"' );

}
return { id, $delimited: true, location: this.tokenLocation( token ) };
const ast = { id, $delimited: true, location: this.tokenLocation( token ) };
ast.location.tokenIndex = token.tokenIndex;
return ast;
}

@@ -781,0 +789,0 @@

@@ -153,2 +153,18 @@ // Official cds-compiler API.

$xsnObjects?: boolean
/**
* Internal option for LSP only!
* If set, each AST gets a `tokenStream` property containing all lexed tokens.
*
* @private
*/
attachTokens?: boolean
/**
* Internal option for LSP only!
* If set, enables some extra checks/work for the CDS LSP.
* May be implicitly set by `$lsp.<api>` functions.
*
* @private
* @since v4.9
*/
lspMode?: boolean
}

@@ -264,4 +280,2 @@

*
* WORK IN PROGRESS
*
* @internal

@@ -271,3 +285,26 @@ * @see _for.effective()

export interface EffectiveCsnOptions extends SqlOptions {
// TODO
/**
* If true, resolve simple type references to their simple base type.
*
* @default true
*/
resolveSimpleTypes?: boolean
/**
* If true, transform projections into ordinary views with SELECT.
*
* @default true
*/
resolveProjections?: boolean
/**
* If true, remap OData annotations to ABAP annotations.
*
* @default false
*/
remapOdataAnnotations?: boolean
/**
* If true, keep '.localized' property in the CSN.
*
* @default false
*/
keepLocalized?: boolean
}

@@ -553,7 +590,2 @@

model?: CSN;
/**
* Used by `cdsc` to indicate whether the message was already printed to stderr.
* @private
*/
hasBeenReported: boolean;
}

@@ -673,8 +705,14 @@

*
* @param config.idInBrackets
* If true, the message ID (if there is one and noMessageId is falsey) will be put in brackets.
* This will be the default in cds-compiler v5.
*
* @param config.noHome
* If true, will _not_ show message's semantic location.
*
* @param config.module
* @param config.moduleForMarker
* If set, downgradable error messages will get a '‹↓›' marker, depending on whether
* the message can be downgraded for the given module.
* the message can be downgraded for the given module. A `‹↑›` is used if the message
* will be an error in the next major cds-compiler release.
* Was called `module` in v4.8.0 and earlier.
*/

@@ -684,2 +722,3 @@ export function messageString(msg: CompileMessage, config?: {

noMessageId?: boolean
idInBrackets?: boolean
noHome?: boolean

@@ -722,5 +761,7 @@ module?: string

*
* @param config.module
* @param config.moduleForMarker
* If set, downgradable error messages will get a '‹↓›' marker, depending on whether
* the message can be downgraded for the given module.
* the message can be downgraded for the given module. A `‹↑›` is used if the message
* will be an error in the next major cds-compiler release.
* Was called `module` in v4.8.0 and earlier.
*

@@ -752,3 +793,3 @@ * @param config.sourceMap

hintExplanation?: boolean
module?: string
moduleForMarker?: string
sourceMap?: Record<string, string>

@@ -868,2 +909,8 @@ sourceLineMap?: Record<string, number[]>

function effective(csn: CSN, options?: EffectiveCsnOptions): CSN;
/**
* Transform the given CSN into one that has the properties required for SEAL
*
* @internal
*/
function seal(csn: CSN, options?: EffectiveCsnOptions): CSN;
}

@@ -1281,3 +1328,9 @@

*/
export function traverseCsn(userFunctions: Record<string, Function>, csn: object|any[]): void;
export function traverseCsn(userFunctions: Record<string, TraverseCsnCallback>, csn: object|any[]): void;
export type TraverseCsnCallback = (
userFunctions: Record<string, TraverseCsnCallback>,
value: any,
csn: any,
prop: string
) => any;

@@ -1284,0 +1337,0 @@ /**

@@ -131,2 +131,3 @@ // Main entry point for the CDS Compiler

effective: (...args) => snapi.for_effective(...args),
seal: (...args) => snapi.for_seal(...args),
},

@@ -133,0 +134,0 @@ to: {

@@ -50,5 +50,8 @@ 'use strict';

*
* @param {object} csn Top-level CSN. You can pass non-dictionary values.
* @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`.
* @param {boolean} sort Whether to sort CSN properties.
* @param {object} csn
* Top-level CSN. You can pass non-dictionary values.
* @param {CSN.Options} options
* CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`.
* @param {boolean} sort
* Whether to sort CSN properties.
*/

@@ -55,0 +58,0 @@ function cloneCsn( csn, options, sort ) {

@@ -233,3 +233,4 @@ // CSN functionality for resolving references

orderBy_set_ref: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
// refs in ORDER BY expr in UNION not really allowed - only with table alias (of outer queries) or $self
// refs in ORDER BY expr in UNION not really allowed
// only with table alias (of outer queries) or $self
orderBy_set_expr: { lexical: query => query.$next, dynamic: false },

@@ -413,4 +414,7 @@ // default: { lexical: query => query, dynamic: 'source' }

function getOriginRaw( art ) {
if (art.type) // TODO: make robust against "linked" = only direct
return artifactRef( art.type, BUILTIN_TYPE );
if (art.type) { // TODO: make robust against "linked" = only direct
return (art.type !== '$self' || csn.definitions.$self)
? artifactRef( art.type, BUILTIN_TYPE )
: getCache( boundActionOrMain( art ), '_parent' );
}
if (typeof art.$origin === 'object') // null, […], {…}

@@ -703,3 +707,4 @@ return getOriginExplicit( art.$origin );

// if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
// throw new ModelError( `Path item 0=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
// throw new ModelError( `Path item 0=${ pathId( path[0] )
// } refers to nothing, scope: ${ scope }`);
links[0].art = art;

@@ -1016,4 +1021,4 @@ for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above

* @param {CSN.Query} query
* @param {CSN.QuerySelect} fromSelect: for query in `from`
* @param {CSN.Query} parentQuery: for a sub query (ex those in `from`)
* @param {CSN.QuerySelect} fromSelect for query in `from`
* @param {CSN.Query} parentQuery for a sub query (ex those in `from`)
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched, parentQuery: CSN.Query) => void} callback

@@ -1142,3 +1147,3 @@ */

if (csnPath.length < 2 || head !== 'definitions' && head !== 'vocabularies')
throw new CompilerAssertion( 'References outside definitions and vocabularies not supported yet');
throw new CompilerAssertion( 'References outside definitions and vocabularies not supported yet' );
const art = csn[head][csnPath[1]];

@@ -1145,0 +1150,0 @@ return {

@@ -9,2 +9,3 @@ 'use strict';

applyTransformationsOnDictionary,
mergeTransformers,
} = require('../transform/db/applyTransformations');

@@ -138,3 +139,4 @@ const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');

const art = artifactRef.from(arg);
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
elements = mergeElementsIntoMap(elements, art.elements, art.$location,
arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
}

@@ -675,7 +677,9 @@ else if (arg.SELECT || arg.SET) {

return false;
const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured')
? !!options.odataForeignKeys : true;
const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
const isNavigable = elementCsn.target
? (elementCsn['@odata.navigable'] === undefined ||
elementCsn['@odata.navigable'] !== undefined && (elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
elementCsn['@odata.navigable'] !== undefined &&
(elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
// Foreign Keys can be ignored

@@ -1130,3 +1134,3 @@ if (elementCsn['@odata.foreignKey4'])

* will be applied.
* applyToElements: Wether to apply annotations to elements or only to artifacts
* applyToElements: Whether to apply annotations to elements or only to artifacts
*/

@@ -1196,3 +1200,4 @@ function applyAnnotationsFromExtensions( csn, config ) {

return artifact.kind === 'entity' &&
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true));
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) ||
hasAnnotationValue(artifact, '@cds.persistence.skip', true));
}

@@ -1376,3 +1381,4 @@

function isDollarSelfOrProjectionOperand( arg ) {
return arg.ref && arg.ref.length === 1 && (arg.ref[0] === '$self' || arg.ref[0] === '$projection');
return arg.ref && arg.ref.length === 1 &&
(arg.ref[0] === '$self' || arg.ref[0] === '$projection');
}

@@ -1417,3 +1423,2 @@

}
return isExpr;

@@ -1440,2 +1445,3 @@ }

applyTransformationsOnDictionary,
mergeTransformers,
setDependencies,

@@ -1442,0 +1448,0 @@ isPersistedOnDatabase,

@@ -210,3 +210,5 @@ // For testing: reveal non-enumerable properties in CSN, display result of csnRefs

function pathRef( parent, prop, path, inspectionPath = csnPath ) {
const inspection = handleError( err => ((err) ? { scope: err.toString() } : inspectRef( inspectionPath )));
const inspection = handleError( err => ((err)
? { scope: err.toString() }
: inspectRef( inspectionPath )));
const {

@@ -213,0 +215,0 @@ links, art, scope, $env,

@@ -41,3 +41,4 @@ // Make internal properties of the XSN / augmented CSN visible

// represent "navigation elements" in _combined as links:
$navElement: (art, parent) => art._parent && parent !== art._parent.elements && art._parent.kind !== 'aspect',
$navElement: (art, parent) => art._parent && parent !== art._parent.elements &&
art._parent.kind !== 'aspect',
// represent mixin in $tableAliases as link:

@@ -44,0 +45,0 @@ mixin: tableAliasAsLink,

@@ -8,4 +8,7 @@ 'use strict';

* @typedef {Object} Layers
* @property {Array[]} layers - An array of arrays, each subarray encompassing one Layer - L0 being layers[0].
* @property {CSN.Artifact[]} leftover - Any artifacts not sorted into a layer due to unmet dependencies - points to there being some error.
* @property {Array[]} layers
* An array of arrays, each subarray encompassing one Layer - L0 being layers[0].
* @property {CSN.Artifact[]} leftover
* Any artifacts not sorted into a layer due to unmet dependencies.
* Points to there being some error.
*/

@@ -26,3 +29,4 @@

const layers = [];
let { zero, nonZero } = _calculateDepth(Object.entries(csn.definitions), _dependents, _dependencies);
let { zero, nonZero } = _calculateDepth(Object.entries(csn.definitions),
_dependents, _dependencies);
while (zero.length !== 0) {

@@ -92,4 +96,6 @@ const currentLayer = [];

*
* @param {{sql: string, csn: CSN.Model}} arg sql: Map of <object name>: "CREATE STATEMENT", csn: Model
* @returns {{name: string, sql: string}[]} Sorted array of artifact name / "CREATE STATEMENTS" pairs
* @param {{sql: string, csn: CSN.Model}} arg
* sql: Map of <object name>: "CREATE STATEMENT", csn: Model
* @returns {{name: string, sql: string}[]}
* Sorted array of artifact name / "CREATE STATEMENTS" pairs
*/

@@ -104,3 +110,5 @@ module.exports = function sortViews({ sql, csn }) {

// keep the "artifact name" - needed for to.hdi sorting
layers.forEach(layer => layer.forEach(objName => result.push({ name: objName, sql: sql[objName], dependents: csn.definitions[objName][_dependents] })));
layers.forEach(layer => layer.forEach(objName => result.push({
name: objName, sql: sql[objName], dependents: csn.definitions[objName][_dependents],
})));
// attach sql artifacts which are not considered during the view sorting algorithm

@@ -107,0 +115,0 @@ // --> this is the case for "ALTER TABLE ADD CONSTRAINT" statements,

@@ -16,2 +16,8 @@ 'use strict';

const relevantProperties = {
doc: true,
'@sql.prepend': true,
'@sql.append': true,
};
/**

@@ -86,3 +92,30 @@ * Compares two models, in HANA-transformed CSN format, to each other.

let hasPrimaryKeyChange = false;
const otherArtifact = beforeModel.definitions[name];
const isPersisted = isPersistedAsTable(artifact);
const isPersistedOther = otherArtifact && isPersistedAsTable(otherArtifact);
// to make it easier to know which views to drop-create
if (isPersistedAsView(artifact) && isPersistedAsView(otherArtifact)) {
// TODO: Check only on artifact.query/projection BUT: Need to manually check for sql-snippets then!
artifact[isChanged] = JSON.stringify(artifact) !== JSON.stringify(otherArtifact);
}
// Looking for added entities and added/deleted/changed elements.
// Parameters: `artifact` from afterModel and `otherArtifact` from beforeModel.
if (!isPersisted) // Artifact not persisted in afterModel.
return;
if (!isPersistedOther) {
extensions[name] = artifact;
return;
}
// Artifact changed?
addElements();
changePropsOrRemoveOrChangeElements();
if (hasPrimaryKeyChange)
changedPrimaryKeys.push(name);
function addElements() {

@@ -216,30 +249,2 @@ const elements = {};

}
const otherArtifact = beforeModel.definitions[name];
const isPersisted = isPersistedAsTable(artifact);
const isPersistedOther = otherArtifact && isPersistedAsTable(otherArtifact);
// to make it easier to know which views to drop-create
if (isPersistedAsView(artifact) && isPersistedAsView(otherArtifact)) {
// TODO: Check only on artifact.query/projection BUT: Need to manually check for sql-snippets then!
artifact[isChanged] = JSON.stringify(artifact) !== JSON.stringify(otherArtifact);
}
// Looking for added entities and added/deleted/changed elements.
// Parameters: `artifact` from afterModel and `otherArtifact` from beforeModel.
if (!isPersisted) // Artifact not persisted in afterModel.
return;
if (!isPersistedOther) {
extensions[name] = artifact;
return;
}
// Artifact changed?
addElements();
changePropsOrRemoveOrChangeElements();
if (hasPrimaryKeyChange)
changedPrimaryKeys.push(name);
};

@@ -380,8 +385,2 @@ }

const relevantProperties = {
doc: true,
'@sql.prepend': true,
'@sql.append': true,
};
/**

@@ -388,0 +387,0 @@ * Returns whether any type parameters differ between two given elements. Ignores whether types themselves differ (`type` property) and ignores

@@ -158,3 +158,4 @@ 'use strict';

inspect [options] <files...> (internal) Inspect the given CDS files.
toEffectiveCsn [options] <files...> (internal) Get an effective CSN for SEAL; requires beta mode
toEffectiveCsn [options] <files...> (internal) Get an effective CSN; requires beta mode
forSeal [options] <files...> (internal) Get a SEAL CSN

@@ -550,2 +551,4 @@ Environment variables

.option('--resolve-projections <val>', { valid: ['true', 'false'] } )
.option('--remap-odata-annotations <val>', { valid: ['true', 'false'] } )
.option('--keep-localized <val>', { valid: ['true', 'false'] } )
.positionalArgument('<files...>')

@@ -565,8 +568,30 @@ .help(`

--resolve-projections <val> Resolve projections:
true: (default) resolve projections to ordinary views with SELECT
true: (default) transform projections into ordinary views with SELECT
false: leave them as real projections
--remap-odata-annotations <val> Remap OData annotations to ABAP annotations:
true: remap annotations
false:(default) leave them as is
--keep-localized <val> Keep '.localized' property in the CSN:
true: property is kept
false:(default) property is deleted
`);
optionProcessor.command('forSeal')
.option('-h, --help')
.option('--remap-odata-annotations <val>', { valid: ['true', 'false'] } )
.positionalArgument('<files...>')
.help(`
Usage: cdsc forSeal [options] <files...>
(internal): Get the SEAL CSN model compiled from the provided CDS files.
Options
-h, --help Show this help text
--remap-odata-annotations <val> Remap OData annotations to ABAP annotations:
true: (default) remap annotations
false: leave them as is
`);
module.exports = {
optionProcessor
};

@@ -100,8 +100,8 @@ /**

error(null, [ 'definitions', artifact.modelName ],
{ name: collidesWith.modelName, prop: namingMode, '#': artifact.modelName.includes('.') ? 'dots' : 'std' },
{
std: 'Artifact name can\'t be mapped to a SQL compliant identifier in naming mode $(PROP) because it conflicts with existing definition $(NAME)',
dots: 'Artifact name containing dots can\'t be mapped to a SQL compliant identifier in naming mode $(PROP) because it conflicts with existing definition $(NAME)',
});
error(null, [ 'definitions', artifact.modelName ], {
name: collidesWith.modelName, prop: namingMode, '#': artifact.modelName.includes('.') ? 'dots' : 'std',
}, {
std: 'Artifact name can\'t be mapped to a SQL compliant identifier in naming mode $(PROP) because it conflicts with existing definition $(NAME)',
dots: 'Artifact name containing dots can\'t be mapped to a SQL compliant identifier in naming mode $(PROP) because it conflicts with existing definition $(NAME)',
});
});

@@ -108,0 +108,0 @@ }

@@ -35,2 +35,3 @@

if (drop && alter)
// eslint-disable-next-line cds-compiler/message-no-quotes
error(null, null, 'Option “--drop” can\'t be combined with “--alter”');

@@ -37,0 +38,0 @@

@@ -347,3 +347,36 @@ 'use strict';

/**
* Merge an array of transformer-objects into a single one, set the this-value of every subfunction to "that"
*
* @param {object[]} transformers transformers
* @param {object} that Value for this
* @returns {object} Remapped transformers.
*/
function mergeTransformers( transformers, that ) {
const remapped = {};
for (const transformer of transformers) {
for (const [ n, fns ] of Object.entries(transformer)) {
if (!remapped[n])
remapped[n] = [];
if (Array.isArray(fns)) {
remapped[n].push((parent, name, prop, path, parentParent) => fns.forEach(
fn => fn.bind(that)(parent, name, prop, path, parentParent)
));
}
else {
remapped[n].push((parent, name, prop, path, parentParent) => fns.bind(that)(parent, name, prop, path, parentParent));
}
}
}
for (const [ n, fns ] of Object.entries(remapped))
remapped[n] = (parent, name, prop, path, parentParent) => fns.forEach(fn => fn.bind(that)(parent, name, prop, path, parentParent));
return remapped;
}
module.exports = {
mergeTransformers,
transformExpression,

@@ -350,0 +383,0 @@ applyTransformations,

@@ -119,3 +119,3 @@ 'use strict';

else if (!onCondition && composition.keys.length > 0) {
throw new CompilerAssertion('Please debug me, an on-condition was expected here, but only found keys');
throw new CompilerAssertion('Debug me, an on-condition was expected here, but only found keys');
}

@@ -122,0 +122,0 @@ }

@@ -101,3 +101,5 @@ 'use strict';

markAsToDummify(artifact, path[1]);
error( null, [ 'definitions', path[1] ], { name: path[1] }, 'Unexpected .expand with to-many association in entity $(NAME)');
rewritten.toMany.forEach(({ art }) => {
error( null, art.$path || [ 'definitions', path[1] ], { name: `${art.$env || path[1]}:${art.ref.map(r => r.id || r)}` }, 'Unexpected .expand with to-many association $(NAME)');
});
}

@@ -179,4 +181,7 @@ else {

error( null, obj.$path, {
id: pathStep, elemref: obj, name,
}, 'Unexpected “@cds.persistence.skip” annotation on Association target $(NAME) of $(ID) in path $(ELEMREF) was skipped because of .expand in conjunction with to-many');
id: pathStep,
elemref: obj,
name,
anno: '@cds.persistence.skip',
}, 'Unexpected $(ANNO) annotation on Association target $(NAME) of $(ID) in path $(ELEMREF) was skipped because of .expand in conjunction with to-many');
}

@@ -183,0 +188,0 @@ }

@@ -31,4 +31,4 @@ 'use strict';

error(null, groupByPath,
{ $reviewed: true },
'Unexpected managed association in GROUP BY for naming mode “hdbcds”');
{ $reviewed: true, keyword: 'GROUP BY', value: 'hdbcds' },
'Unexpected managed association in $(KEYWORD) for naming mode $(VALUE)');
continue;

@@ -35,0 +35,0 @@ }

@@ -7,3 +7,3 @@ 'use strict';

const { implicitAs } = require('../../model/csnRefs');
const { setProp } = require('../../base/model');
const { setProp, isBetaEnabled } = require('../../base/model');
const { getTransformers } = require('../transformUtils');

@@ -25,5 +25,6 @@

* @param {object} csnUtils
* @param {object} options
* @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition applying the where-condition to views.
*/
function getViewDecorator( csn, messageFunctions, csnUtils ) {
function getViewDecorator( csn, messageFunctions, csnUtils, options ) {
const { info } = messageFunctions;

@@ -57,3 +58,5 @@ const { get$combined } = csnUtils;

if (from[0].source === to[0].source && from[0].parent === to[0].parent) {
if (!hasFalsyTemporalAnnotations(normalizedQuery.query.SELECT, artifact.elements, from[0], to[0])) {
const omitWhereClause = isBetaEnabled(options, 'temporalRawProjection') &&
hasFalsyTemporalAnnotations(normalizedQuery.query.SELECT, artifact.elements, from[0], to[0]);
if (!omitWhereClause) {
const fromPath = {

@@ -60,0 +63,0 @@ ref: [

@@ -619,6 +619,6 @@ 'use strict';

return '';
return error(null, xpr.$path, '$env with number is not handled yet - please report this error!');
return error(null, xpr.$path, '$env with number is not handled yet - report this error!');
}
return error(null, xpr.$path, 'Boolean $env is not handled yet - please report this error!');
return error(null, xpr.$path, 'Boolean $env is not handled yet - report this error!');
}

@@ -625,0 +625,0 @@ else if (xpr.ref) {

'use strict';
const { isBetaEnabled } = require('../../base/model');
const { CompilerAssertion } = require('../../base/error');
const { getUtils, isAspect } = require('../../model/csnUtils');
const {
getUtils, isAspect, mergeTransformers, applyTransformations,
} = require('../../model/csnUtils');
const transformUtils = require('../transformUtils');

@@ -17,2 +17,3 @@ const flattening = require('../db/flattening');

const misc = require('./misc');
const annotations = require('./annotations');
const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');

@@ -34,5 +35,2 @@ const { cloneFullCsn } = require('../../model/cloneCsn');

function effectiveCsn( model, options, messageFunctions ) {
if (!isBetaEnabled(options, 'effectiveCsn'))
throw new CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
const csn = cloneFullCsn(model, options);

@@ -85,4 +83,4 @@ delete csn.vocabularies; // must not be set for effective CSN

generateDrafts(csn, options, '_', messageFunctions);
misc.attachPersistenceName(csn, options, csnUtils);
misc.removeDefinitionsAndProperties(csn, options);
const transformers = mergeTransformers([ misc.attachPersistenceName(csn, options, csnUtils), options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options) ], null);
applyTransformations(csn, transformers, [], { skipIgnore: false });

@@ -89,0 +87,0 @@ if (!options.resolveProjections)

'use strict';
const {
forEachDefinition, forEachMemberRecursively, getArtifactDatabaseNameOf, getElementDatabaseNameOf, applyTransformations,
getArtifactDatabaseNameOf, getElementDatabaseNameOf,
} = require('../../model/csnUtils');

@@ -12,2 +12,3 @@ /**

* @param {object} csnUtils
* @returns {object}
*/

@@ -17,9 +18,25 @@ function attachPersistenceName( csn, options, csnUtils ) {

forEachDefinition(csn, (artifact, artifactName) => {
if (artifact.kind === 'entity') {
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
/**
*
* @param {object} parent
* @param {string} prop
* @param {object} dict
* @param {CSN.Path} path
*/
function addToEachMember( parent, prop, dict, path ) {
const artifact = csn.definitions[path[1]];
if (artifact?.kind === 'entity') {
for (const memberName in dict)
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), dict[memberName]);
}
}
forEachMemberRecursively(artifact, (member, memberName) => addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member), [ 'definitions', artifactName ]);
}
});
return {
kind: (parent, prop, kind, path) => {
if (kind === 'entity')
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(path[1], options.sqlMapping, csn, options.sqlDialect), parent);
},
elements: addToEachMember,
params: addToEachMember,
};
}

@@ -47,6 +64,7 @@

* @param {CSN.Options} options
* @returns {object}
* @todo Callback-like architecture and merge with persistence name?
*/
function _removeDefinitionsAndProperties( csn, options ) {
const killers = {
const transformers = {
$ignore: (a, b, c, path, parentParent) => {

@@ -80,9 +98,12 @@ const tail = path[path.length - 1];

includes: killProp,
localized: killProp,
enum: killProp,
keys: killProp,
excluding: killProp, // * is resolved, so has no effect anymore
targetAspect: killProp,
};
applyTransformations(csn, killers, [], { skipIgnore: false });
if (!options.keepLocalized)
transformers.localized = killProp;
return transformers;
}

@@ -89,0 +110,0 @@

@@ -76,3 +76,3 @@ 'use strict';

// copy the model as we don't want to change the input model
let csn = cloneFullCsn(inputModel, options);
const csn = cloneFullCsn(inputModel, options);
messageFunctions.setModel(csn);

@@ -188,10 +188,15 @@

if (!structuredOData) {
expansion.expandStructureReferences(csn, options, '_', { error, info, throwWithAnyError }, csnUtils, { skipArtifact: isExternalServiceMember });
expansion.expandStructureReferences(csn, options, '_',
{ error, info, throwWithAnyError }, csnUtils,
{ skipArtifact: isExternalServiceMember });
const resolved = new WeakMap();
const { inspectRef, effectiveType } = csnRefs(csn);
const { adaptRefs, transformer: refFlattener } = flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
const { adaptRefs, transformer: refFlattener } =
flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
flattening.allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options);
flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs, inspectRef, effectiveType, csnUtils, error, options,
flattening.allInOneFlattening(csn, refFlattener, adaptRefs,
inspectRef, isExternalServiceMember, error, csnUtils, options);
flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs,
inspectRef, effectiveType, csnUtils, error, options,
{ //skip: ['action', 'aspect', 'event', 'function', 'type'],

@@ -213,2 +218,11 @@ skipArtifact: isExternalServiceMember,

}
if(def.actions) {
Object.values(def.actions).forEach((action) => {
if(action.$flatAnnotations) {
Object.entries(action.$flatAnnotations).forEach(([an, av]) => {
action[an] = av;
});
}
});
}
});

@@ -219,3 +233,4 @@ }

// see db/views.js::addForeignKeysToColumns
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_', !structuredOData, csnUtils,{ skipArtifact: isExternalServiceMember });
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_',
!structuredOData, csnUtils,{ skipArtifact: isExternalServiceMember });

@@ -259,3 +274,4 @@ // Allow using managed associations as steps in on-conditions to access their fks

if (options.sqlMapping && !(def.kind in skipPersNameKinds))
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
// hana to allow naming mode "hdbcds"
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana');

@@ -262,0 +278,0 @@ forEachMemberRecursively(def, (member, memberName, propertyName) => {

@@ -186,3 +186,3 @@ 'use strict';

// assoc2join eventually rewrites the table aliases
temporal.getViewDecorator(csn, messageFunctions, csnUtils),
temporal.getViewDecorator(csn, messageFunctions, csnUtils, options),
// check unique constraints - further processing is done in rewriteUniqueConstraints

@@ -189,0 +189,0 @@ assertUnique.prepare(csn, options, messageFunctions)

'use strict';
const { makeMessageFunction } = require('../base/messages');
const { setProp, isDeprecatedEnabled} = require('../base/model');
const { setProp, isDeprecatedEnabled, isBetaEnabled} = require('../base/model');
const { forEachKey } = require('../utils/objectUtils');

@@ -750,3 +750,3 @@ const { cleanSymbols } = require('../base/cleanSymbols.js');

/**
* Copy @cds.persistence.exists/skip annotations from the source to
* Copy @cds.persistence.skip annotations from the source to
* the target. Ignores existing annotations on the _target_.

@@ -759,10 +759,11 @@ *

function copyPersistenceAnnotations(target, source, options) {
const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
const copyExists = !isBetaEnabled(options, 'v5preview') &&
!isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
forEachKey(source, anno => {
// Note:
// Because `.exists` is copied to the convenience view, it could
// v3/v4: Because `.exists` is copied to the convenience view, it could
// lead to some localization views referencing non-existing ones.
// But that is the contract: User says that it already exists!
// In v2, `.exists` was never copied.
if (anno === annoPersistenceSkip || (!doNotCopyExists && anno === '@cds.persistence.exists'))
// v2/>=v5, `.exists` is never copied.
if (anno === annoPersistenceSkip || (copyExists && anno === '@cds.persistence.exists'))
target[anno] = source[anno];

@@ -769,0 +770,0 @@ });

@@ -8,3 +8,3 @@ 'use strict';

const transformUtils = require('../transformUtils');
const { setProp, isBetaEnabled } = require('../../base/model');
const { setProp } = require('../../base/model');
const { applyTransformationsOnDictionary,

@@ -18,12 +18,7 @@ applyTransformationsOnNonDictionary } = require('../db/applyTransformations.js');

function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options) {
const isExprAnno = (obj, pn) => {
return isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
}
forEachDefinition(csn, (def, defName) => {
if(def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
if (def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
['elements', 'params'].forEach(dictName => {
const dict = def[dictName];
if(dict) {
if (dict) {
const csnPath = ['definitions', defName, dictName];

@@ -34,11 +29,11 @@ const orderedElementList = [];

const location = [ ...csnPath, childName ];
let rootPrefix = [ defName ];
const rootPrefix = [ defName ];
let resolvedElt = child;
let typeIdx = 0;
if(child.type && !child.elements) {
if (child.type && !child.elements) {
resolvedElt = csnUtils.getFinalTypeInfo(child.type);
if(resolvedElt.elements)
if (resolvedElt.elements)
typeIdx = rootPrefix.length + 1;
}
if(resolvedElt.elements) {
if (resolvedElt.elements) {
const flattenedSubTree = recurseIntoElement(dictName, child, resolvedElt,

@@ -63,6 +58,6 @@ !!child.notNull, location, [ ...rootPrefix, childName ], typeIdx);

const flatDict = orderedElementList.reduce((elements, [ flatEltName, flatElt ]) => {
if(flatElt.items) {
if (flatElt.items) {
// rewrite annotation paths inside items.elements
forEachMemberRecursively(flatElt.items, (elt, _eltName, _prop, path) => {
const exprAnnos = Object.keys(elt).filter(pn => isExprAnno(elt, pn));
const exprAnnos = Object.keys(elt).filter(pn => findAnnotationExpression(elt, pn));
flattenAndPrefixExprPaths(elt, exprAnnos, elt.$path, path, 0, true);

@@ -78,6 +73,62 @@ }, [ flatEltName ], true, { pathWithoutProp: true } );

});
// entity annotations
const flatAnnos = Object.create(null);
const annoNames = copyAnnotations(def, flatAnnos).filter(an => isExprAnno(def, an));
const annoNames = copyAnnotations(def, flatAnnos).filter(an => findAnnotationExpression(def, an));
flattenAndPrefixExprPaths(flatAnnos, annoNames, [ 'definitions', defName ], [ defName ], 0);
setProp(def, '$flatAnnotations', flatAnnos);
// explicit binding parameter of bound action
if (def.actions) {
const special$self = !csn?.definitions?.$self && '$self';
Object.entries(def.actions).forEach(([actionName, action]) => {
if (action.params) {
const params = Object.entries(action.params);
const firstParam = params[0][1];
const type = firstParam?.items?.type || firstParam?.type;
if (type === special$self) {
const bindingParamName = params[0][0];
const markBindingParam = {
ref: (parent, prop, xpr) => {
if ((xpr[0].id || xpr[0]) === bindingParamName)
setProp(parent, '$bparam', true)
},
};
const refCheck = {
ref: (elemref, prop, xpr, path) => {
const { art, scope } = inspectRef(path);
if (scope !== '$magic' && art) {
const ft = csnUtils.getFinalTypeInfo(art.type);
if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
}
}
}
};
const flatAnnos = Object.create(null);
const annoNames = copyAnnotations(action, flatAnnos).filter(an => findAnnotationExpression(action, an));
annoNames.forEach((an) => {
refCheck.anno = an;
transformExpression(flatAnnos, an,
[ markBindingParam, refCheck, refFlattener ],
[ 'definitions', defName, 'actions', actionName ]);
adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
adaptRefs.length = 0;
});
setProp(action, '$flatAnnotations', flatAnnos);
forEachMemberRecursively(action, (member, memberName, prop, path, _parent) => {
const exprAnnos = Object.keys(member).filter(pn => findAnnotationExpression(member, pn));
exprAnnos.forEach((pn) => {
refCheck.anno = pn;
transformExpression(member, pn, [ markBindingParam, refCheck, refFlattener ], path);
adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
adaptRefs.length = 0;
});
},
[ 'definitions', defName, 'actions', actionName ]);
}
}
});
}
}

@@ -88,5 +139,5 @@ });

const eltName = rootPrefix[rootPrefix.length-1];
if(!resolvedElt.elements) {
if (!resolvedElt.elements) {
const flatElt = cloneElt(scope, elt, location, rootPrefix, typeIdx);
if(rootPathIsNotNull)
if (rootPathIsNotNull)
flatElt.notNull = true;

@@ -101,5 +152,5 @@ else

resolvedElt = child;
if(child.type && !child.elements) {
if (child.type && !child.elements) {
resolvedElt = csnUtils.getFinalTypeInfo(child.type);
if(resolvedElt.elements)
if (resolvedElt.elements)
typeIdx = rootPrefix.length + 1;

@@ -117,3 +168,3 @@ }

}).filter(([name, flatElt]) =>{
if(duplicateDict[name]) {
if (duplicateDict[name]) {
error('name-duplicate-element', location,

@@ -154,3 +205,3 @@ { '#': 'flatten-element-gen', name });

// of typePathRoot (which could be some completely different path)
const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => isExprAnno(flatElt, pn));
const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => findAnnotationExpression(flatElt, pn));
flattenAndPrefixExprPaths(flatElt, exprAnnoNames, elt.$path, rootPrefix, typeIdx);

@@ -177,5 +228,5 @@ // Copy selected type properties

const [ nonAnnoProps, exprAnnoProps ] = Object.keys(flatElt).reduce((acc, pn) => {
if(pn[0] !== '@' && pn !== 'value')
if (pn[0] !== '@' && pn !== 'value')
acc[0].push(pn);
if(isExprAnno(flatElt, pn) || pn === 'value')
if (findAnnotationExpression(flatElt, pn) || pn === 'value')
acc[1].push(pn);

@@ -194,14 +245,14 @@ return acc;

function retypeCloneWithFinalBaseType(elt, location) {
if(elt.type &&
if (elt.type &&
!isBuiltinType(elt.type) &&
!isODataV4BuiltinFromService(elt.type, location)
&& !isItemsType(elt.type)) {
let resolvedType = csnUtils.getFinalTypeInfo(elt);
const resolvedType = csnUtils.getFinalTypeInfo(elt);
delete resolvedType.kind;
if(resolvedType.items)
if (resolvedType.items)
delete resolvedType.type;
if(elt.items) {
if(resolvedType.items) {
if (elt.items) {
if (resolvedType.items) {
elt.items = resolvedType;

@@ -214,3 +265,3 @@ delete elt.items.type;

else {
if(resolvedType.items) {
if (resolvedType.items) {
elt.items = resolvedType.items;

@@ -225,3 +276,3 @@ delete elt.type;

function isItemsType(typeName) {
let typeDef = csn.definitions[typeName];
const typeDef = csn.definitions[typeName];
return !!typeDef?.items;

@@ -236,3 +287,3 @@ }

let finalBaseType = csnUtils.getFinalTypeInfo(typeName).type;
if(!isBuiltinType(finalBaseType)) {
if (!isBuiltinType(finalBaseType)) {
const typeDef = csn.definitions[finalBaseType];

@@ -258,6 +309,14 @@ finalBaseType = typeDef?.items?.type || typeDef?.type;

ref: (elemref, prop, xpr, path) => {
const { art } = (elemref._art ? { art: elemref._art } : inspectRef(path));
const ft = csnUtils.getFinalTypeInfo(art?.type);
if(!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, name: refCheck.eltLocationStr, '#': 'flatten_builtin' });
const { art, scope } = inspectRef(path);
if (scope !== '$magic' && art) {
const ft = csnUtils.getFinalTypeInfo(art.type);
if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path,
{
anno: refCheck.anno,
elemref,
name: refCheck.eltLocationStr,
'#': 'flatten_builtin'
});
}
}

@@ -269,3 +328,6 @@ }

const [head, ...tail ] = rootPrefix;
return `${head}:${tail.join('.')}`;
if(tail.length)
return `${head}:${tail.join('.')}`;
else
return `${head}`;
})();

@@ -276,19 +338,21 @@

const head = xpr[0].id || xpr[0];
if(typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
if (typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
const [xprHead, ...xprTail] = xpr.slice(1, xpr.length);
if(xprHead.id) {
xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
parent[prop] = [ xprHead, ...xprTail ];
if(xprHead) {
if (xprHead.id) {
xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
parent[prop] = [ xprHead, ...xprTail ];
}
else
parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
if (carrier.$scope === 'params')
parent.param = true;
else
parent[prop].unshift('$self');
}
else
parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
if(carrier.$scope === 'params')
parent.param = true;
else
parent[prop].unshift('$self');
}
else if(rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
else if (rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
const [xprHead, ...xprTail] = xpr;
if(!refParentIsItems) {
if(xprHead.id) {
if (!refParentIsItems) {
if (xprHead.id) {
xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');

@@ -302,3 +366,3 @@ parent[prop] = [ xprHead, ...xprTail ];

parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
if(carrier.$scope === 'params')
if (carrier.$scope === 'params')
parent.param = true;

@@ -341,3 +405,4 @@ else

function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType, csnUtils, error, options, iterateOptions = {} ) {
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
csnUtils, error, options, iterateOptions = {} ) {

@@ -347,13 +412,18 @@ // All anno path flattening is already done, don't do it on locations where we don't want it!

forEachDefinition(csn, (def, defName) => {
if(def.kind === 'entity') {
if (def.kind === 'entity') {
['query', 'projection'].forEach(dictName => {
applyTransformationsOnNonDictionary(csn.definitions[defName], dictName, refFlattener, iterateOptions, [ 'definitions', defName ]);
applyTransformationsOnNonDictionary(csn.definitions[defName],
dictName, refFlattener, iterateOptions,
[ 'definitions', defName ]);
})
if(csn.definitions[defName].actions)
applyTransformationsOnDictionary(csn.definitions[defName].actions, refFlattener, iterateOptions, [ 'definitions', defName, 'actions' ]);
if (csn.definitions[defName].actions)
applyTransformationsOnDictionary(csn.definitions[defName].actions,
refFlattener, iterateOptions,
[ 'definitions', defName, 'actions' ]);
}
if(['type'].includes(def.kind)) {
if (['type'].includes(def.kind)) {
typeNames.push(defName);
applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, iterateOptions, [ 'definitions' ]);
applyTransformationsOnNonDictionary(csn.definitions,
defName, refFlattener, iterateOptions, [ 'definitions' ]);
}

@@ -363,33 +433,36 @@ });

adaptRefs.length = 0;
if(isBetaEnabled(options, 'odataPathsInAnnotationExpressions')) {
const refCheck = {
ref: (elemref, prop, xpr, path) => {
const { links, art } = (elemref._links && elemref._art ? { links: elemref._links, art: elemref._art } : inspectRef(path) );
let i = links.length-2;
const getProp = (propName) =>
(links[i].art?.[propName] ||
effectiveType(links[i].art)[propName]);
const refCheck = {
ref: (elemref, prop, xpr, path) => {
const { links, art } = (elemref._links && elemref._art
? { links: elemref._links, art: elemref._art }
: inspectRef(path) );
const ft = csnUtils.getFinalTypeInfo(art?.type);
let target = undefined;
for(; i >= 0 && !getProp('items') && !target; i--) {
target = getProp('target');
}
if(target && csn.definitions[target].$flatelements && !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
}
let i = links.length-2;
const getProp = (propName) =>
(links[i].art?.[propName] ||
effectiveType(links[i].art)[propName]);
const ft = csnUtils.getFinalTypeInfo(art?.type);
let target = undefined;
for(; i >= 0 && !getProp('items') && !target; i--) {
target = getProp('target');
}
if (target && csn.definitions[target].$flatelements
&& !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path,
{ anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
}
}
typeNames.forEach(tn => {
forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
refCheck.anno = pn;
transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
});
}, [ 'definitions', tn ]);
})
adaptRefs.forEach(fn => fn(true, 1));
adaptRefs.length = 0;
}
typeNames.forEach(tn => {
forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
refCheck.anno = pn;
transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
});
}, [ 'definitions', tn ]);
})
adaptRefs.forEach(fn => fn(true, 1));
adaptRefs.length = 0;
}

@@ -423,23 +496,28 @@

const lastRef = ref[ref.length - 1];
const fn = (suspend=false, suspendPos=0) => {
const scopedPath = [ ...parent.$path ];
// TODO: If foreign key annotations should be assigned via
// full path into target, uncomment this line and
// comment/remove setProp in expansion.js
// setProp(parent, '$structRef', parent.ref);
parent.ref = flattenStructStepsInRef(ref, scopedPath, links, scope, resolvedLinkTypes, suspend, suspendPos);
resolved.set(parent, { links, art, scope });
// Explicitly set implicit alias for things that are now flattened - but only in columns
// TODO: Can this be done elegantly during expand phase already?
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
delete parent.as;
delete parent.$implicitAlias;
const fn = (suspend = false, suspendPos = 0,
refFilter = (_parent) => true) => {
if (refFilter(parent)) {
const scopedPath = [ ...parent.$path ];
// TODO: If foreign key annotations should be assigned via
// full path into target, uncomment this line and
// comment/remove setProp in expansion.js
// setProp(parent, '$structRef', parent.ref);
parent.ref = flattenStructStepsInRef(ref,
scopedPath, links, scope, resolvedLinkTypes,
suspend, suspendPos, parent.$bparam);
resolved.set(parent, { links, art, scope });
// Explicitly set implicit alias for things that are now flattened - but only in columns
// TODO: Can this be done elegantly during expand phase already?
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
delete parent.as;
delete parent.$implicitAlias;
}
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
else if (parent.ref[parent.ref.length - 1] !== lastRef &&
(insideColumns(scopedPath) || insideKeys(scopedPath)) &&
!parent.as) {
parent.as = lastRef;
}
}
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
else if (parent.ref[parent.ref.length - 1] !== lastRef &&
(insideColumns(scopedPath) || insideKeys(scopedPath)) &&
!parent.as) {
parent.as = lastRef;
}

@@ -453,3 +531,6 @@ /**

function insideColumns( path ) {
return path.length >= 3 && (path[path.length - 3] === 'SELECT' || path[path.length - 3] === 'projection') && path[path.length - 2] === 'columns';
return path.length >= 3
&& (path[path.length - 3] === 'SELECT'
|| path[path.length - 3] === 'projection')
&& path[path.length - 2] === 'columns';
}

@@ -463,3 +544,5 @@ /**

function insideKeys( path ) {
return path.length >= 3 && path[path.length - 2] === 'keys' && typeof path[path.length - 1] === 'number';
return path.length >= 3
&& path[path.length - 2] === 'keys'
&& typeof path[path.length - 1] === 'number';
}

@@ -466,0 +549,0 @@ };

@@ -91,3 +91,3 @@ 'use strict';

if (def.items.type && !isBuiltinType(def.items.type)) {
let finalBaseType = csnUtils.getFinalTypeInfo(def.items.type);
const finalBaseType = csnUtils.getFinalTypeInfo(def.items.type);
if (finalBaseType?.elements) {

@@ -94,0 +94,0 @@ def.items.elements = cloneCsnDict(finalBaseType.elements, options);

@@ -11,3 +11,3 @@ 'use strict';

const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
const { getNamespace, copyAnnotations, findAnnotationExpression,
const { getNamespace, copyAnnotations, findAnnotationExpression,
forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');

@@ -81,5 +81,8 @@ const { isBuiltinType } = require('../../base/builtins');

forEachMember(def, (element, elementName, propertyName, path) => {
if (propertyName === 'elements' || propertyName === 'params') {
if (propertyName === 'elements') {
const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, newTypeName, defName, path);
exposeTypeOf(element, element.key, elementName, defName, serviceName, newTypeName, defName, path);
} else if (propertyName === 'params') {
const newTypeName = getNewTypeName(element, elementName, `ep_${defName.replace(/\./g, '_')}`, serviceName);
exposeTypeOf(element, true, elementName, defName, serviceName, newTypeName, defName, path);
}

@@ -90,6 +93,6 @@ }, path);

if (def.kind === 'action' || def.kind === 'function') {
exposeTypesOfAction(def, defName, defName, serviceName, path);
exposeTypesOfAction(def, defName, defName, serviceName, path, false);
}
def.actions && Object.entries(def.actions).forEach(([actionName, action]) => {
exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]));
exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]), true);
});

@@ -128,3 +131,3 @@ }

*/
function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
function exposeTypesOfAction(action, actionName, defName, serviceName, path, isBound) {
if (action.returns) {

@@ -137,3 +140,3 @@ const artificialName = `return_${actionName.replace(/\./g, '_')}`;

action.params && Object.entries(action.params).forEach(([paramName, param]) => {
const artificialName = `param_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
const artificialName = `${isBound ? 'bap' : 'ap'}_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);

@@ -262,3 +265,3 @@ exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['params', paramName]));

newType = Object.create(null);
for(let n in typeDef) {
for(const n in typeDef) {
newType[n] = typeDef[n];

@@ -302,3 +305,3 @@ }

let typeDef = node;
let elements = (node.items?.elements || node.elements)
const elements = (node.items?.elements || node.elements)
// anonymous structured type

@@ -325,3 +328,3 @@ if(elements)

else {
throw new CompilerAssertion(`Please debug me: ${typeName} not found`);
throw new CompilerAssertion(`Debug me: ${typeName} not found`);
}

@@ -383,3 +386,3 @@ }

function getAnonymousTypeNameInMultiSchema(typeName, parentName) {
let currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
const currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
const newSchemaName = currPrefix || fallBackSchemaName;

@@ -415,3 +418,3 @@ // new type name without any prefixes

Object.entries(elements).forEach(([elemName, element]) => {
let cloned = cloneCsnNonDict(element, options);
const cloned = cloneCsnNonDict(element, options);
// if this was an anonymous sub element of a key, mark it as not nullable

@@ -418,0 +421,0 @@ if(isAnonymous && isKey && !cloned.key) {

@@ -61,3 +61,3 @@ // @ts-nocheck

else {
for(let n in xpr) {
for(const n in xpr) {
// xpr could be an array with polluted prototype

@@ -218,3 +218,3 @@ if (Object.hasOwnProperty.call(xpr, n))

if(b >= 0) {
let token = [ 'between' ];
const token = [ 'between' ];
not = (xpr[b-1] === 'not');

@@ -317,3 +317,3 @@ if(not)

// return xpr;
for(let n in xpr) {
for(const n in xpr) {
// xpr could be an array with polluted prototype

@@ -355,3 +355,3 @@ if (!Object.hasOwnProperty.call(xpr, n))

while(p >= 0) {
let rhs = next(xpr, s, p, state);
const rhs = next(xpr, s, p, state);
naryExpr.push(...op, rhs);

@@ -358,0 +358,0 @@ if(state.array)

@@ -165,10 +165,10 @@ 'use strict';

function createForeignKeyElement(assoc, assocName, foreignKey, artifact, artifactName, path) {
let result = {};
const result = {};
// FIXME: Duplicate code (postfix is added herein, can this be optimized?)
// Assemble foreign key element name from assoc name, '_' and foreign key name/alias
let foreignKeyElementName = `${assocName.replace(/\./g, pathDelimiter)}${pathDelimiter}${foreignKey.as || foreignKey.ref.join(pathDelimiter)}`;
const foreignKeyElementName = `${assocName.replace(/\./g, pathDelimiter)}${pathDelimiter}${foreignKey.as || foreignKey.ref.join(pathDelimiter)}`;
let fkArtifact = inspectRef(path).art;
const fkArtifact = inspectRef(path).art;
newForeignKey(fkArtifact, foreignKeyElementName);

@@ -192,3 +192,3 @@

let foreignKeyElement = createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName);
const foreignKeyElement = createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName);

@@ -234,3 +234,3 @@ // FIXME: must this code go into createRealFK?

function flattenStructuredElement(elem, elemName, parentElementPath=[], pathInCsn=[]) {
let elementPath=parentElementPath.concat(elemName); // elementPath contains only element names without the csn structure node names
const elementPath=parentElementPath.concat(elemName); // elementPath contains only element names without the csn structure node names
// in case the element is of user defined type => take the definition of the type

@@ -247,3 +247,3 @@ // for type of 'x' -> elem.type is an object, not a string -> use directly

// leave that work to the receiver of this result
let result = Object.create(null);
const result = Object.create(null);
const addGeneratedFlattenedElement = (e, eName) => {

@@ -258,6 +258,6 @@ if (result[eName])

// Descend recursively into structured children
let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
for (let grandChildName in grandChildElems) {
let flatElemName = elemName + pathDelimiter + grandChildName;
let flatElem = grandChildElems[grandChildName];
const grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
for (const grandChildName in grandChildElems) {
const flatElemName = elemName + pathDelimiter + grandChildName;
const flatElem = grandChildElems[grandChildName];
addGeneratedFlattenedElement(flatElem, flatElemName);

@@ -273,4 +273,4 @@ // TODO: check with values. In CSN such elements have only "@Core.Computed": true

// Primitive child - clone it and restore its cross references
let flatElemName = elemName + pathDelimiter + childName;
let flatElem = cloneCsnNonDict(childElem, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
const flatElemName = elemName + pathDelimiter + childName;
const flatElem = cloneCsnNonDict(childElem, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
// Don't take over notNull from leaf elements

@@ -311,3 +311,3 @@ delete flatElem.notNull;

props.push('localized');
for (let p of props) {
for (const p of props) {
if (elem[p]) {

@@ -334,5 +334,7 @@ flatElem[p] = elem[p];

* @param {bool} [suspend] suspend flattening by caller until association path step
* @param {int} [suspendPos] suspend if starting pos is lower or equal to suspendPos and suspend is true
* @param {bool} [revokeAtSuspendPos] revoke suspension after suspendPos (binding parameter path use case)
* @returns {string[]}
*/
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), suspend=false, suspendPos=0) {
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), suspend=false, suspendPos=0, revokeAtSuspendPos=false) {
// Refs of length 1 cannot contain steps - no need to check

@@ -379,5 +381,6 @@ if (ref.length < 2 || (scope === '$self' && ref.length === 2)) {

result.push(ref[i]);
suspend ||= !!art('items');
}
// revoke items suspension for next assoc step
if(suspend && art('target'))
if(suspend && art('target') || (revokeAtSuspendPos && i === suspendPos))
suspend = false;

@@ -483,7 +486,7 @@

function createExposingProjection(art, artName, projectionId, service) {
let projectionAbsoluteName = `${service}.${projectionId}`;
const projectionAbsoluteName = `${service}.${projectionId}`;
// Create elements matching the artifact's elements
let elements = Object.create(null);
const elements = Object.create(null);
art.elements && Object.entries(art.elements).forEach(([elemName, artElem]) => {
let elem = Object.assign({}, artElem);
const elem = Object.assign({}, artElem);
// Transfer xrefs, that are redirected to the projection

@@ -498,3 +501,3 @@ // TODO: shall we remove the transferred elements from the original?

let query = {
const query = {
'SELECT': {

@@ -509,3 +512,3 @@ 'from': {

// Assemble the projection itself and add it into the model
let projection = {
const projection = {
'kind': 'entity',

@@ -516,3 +519,3 @@ projection: query.SELECT, // it is important that projection and query refer to the same object!

// copy annotations from art to projection
for (let a of Object.keys(art).filter(x => x.startsWith('@'))) {
for (const a of Object.keys(art).filter(x => x.startsWith('@'))) {
projection[a] = art[a];

@@ -622,3 +625,3 @@ }

}
let result = {
const result = {
[elemName]: {

@@ -651,4 +654,4 @@ type: typeName

function createAssociationElement(elemName, target, isManaged = false) {
let elem = createScalarElement(elemName, 'cds.Association', false, undefined);
let assoc = elem[elemName];
const elem = createScalarElement(elemName, 'cds.Association', false, undefined);
const assoc = elem[elemName];
assoc.target = target;

@@ -658,6 +661,6 @@

assoc.keys = [];
let targetArt = getCsnDef(target);
const targetArt = getCsnDef(target);
targetArt.elements && Object.entries(targetArt.elements).forEach(([keyElemName, keyElem]) => {
if (keyElem.key) {
let foreignKey = createForeignKey(keyElemName, keyElem);
const foreignKey = createForeignKey(keyElemName, keyElem);
addForeignKey(foreignKey, assoc);

@@ -719,3 +722,3 @@ }

}
let elemName = Object.keys(elem)[0];
const elemName = Object.keys(elem)[0];
// Element must not exist

@@ -757,3 +760,3 @@ if (artifact.elements[elemName]) {

let result = Object.create(null);
const result = Object.create(null);
result[elementName] = {};

@@ -771,3 +774,3 @@ elem && Object.entries(elem).forEach(([prop, value]) => {

// Assemble the action
let result = {
const result = {
[actionName]: {

@@ -778,3 +781,3 @@ kind: 'action'

let action = result[actionName];
const action = result[actionName];

@@ -814,3 +817,3 @@ if (returnTypeName) {

let actionName = Object.keys(action)[0];
const actionName = Object.keys(action)[0];
// Element must not exist

@@ -832,3 +835,3 @@ if (!artifact.actions[actionName]) {

function extractValidFromToKeyElement(element, path) {
let validFroms = [], validTos = [], validKeys = [];
const validFroms = [], validTos = [], validKeys = [];
if (hasAnnotationValue(element, '@cds.valid.from')) {

@@ -903,3 +906,3 @@ validFroms.push({ element, path: [...path] });

callback(artifact, path);
let elements = artifact.elements;
const elements = artifact.elements;
if (elements) {

@@ -918,3 +921,3 @@ path.push('elements', null);

function renameAnnotation(node, fromName, toName) {
let annotation = node && node[fromName];
const annotation = node && node[fromName];
// Sanity checks

@@ -1066,3 +1069,3 @@ if (!fromName.startsWith('@')) {

if(followMgdAssoc && art.target && art.keys) {
let rc = [];
const rc = [];
for(const k of art.keys) {

@@ -1085,3 +1088,3 @@ const nps = { ref: k.ref.map(p => fullRef ? { id: p } : p ) };

if(elements) {
let rc = []
const rc = []
Object.entries(elements).forEach(([en, elt]) => {

@@ -1135,3 +1138,3 @@ const nps = { ref: [ (fullRef ? { id: en, _art: elt } : en )] };

function expand(expr, location) {
let rc = [];
const rc = [];
for(let i = 0; i < expr.length; i++)

@@ -1138,0 +1141,0 @@ {

@@ -12,3 +12,3 @@ 'use strict';

} = require('../../model/csnUtils');
const { isBuiltinType } = require('../../base/builtins');
const { isBuiltinType, propagationRules } = require('../../base/builtins');
const {

@@ -33,20 +33,6 @@ forEachValue, forEach,

const definitionPropagationRules = {
'@cds.autoexpose': onlyViaArtifact,
'@cds.external': skip,
'@fiori.draft.enabled': onlyViaArtifact,
__proto__: null,
'@': nullStopsPropagation,
// Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
elements: onlyTypeDef,
'@cds.persistence.exists': skip,
'@cds.persistence.table': skip,
'@cds.persistence.calcview': skip,
'@cds.persistence.udf': skip,
'@cds.persistence.skip': notWithPersistenceTable,
'@sql.append': skip,
'@sql.prepend': skip,
'@sql.replace': skip,
'@Analytics.hidden': skip,
'@Analytics.visible': skip,
'@cds.autoexposed': skip,
'@cds.redirection.target': skip,
type: always,

@@ -70,2 +56,12 @@ doc: nullStopsPropagation,

const ruleToFunction = {
__proto__: null,
never: skip,
onlyViaArtifact,
notWithPersistenceTable,
};
for (const rule in propagationRules)
definitionPropagationRules[rule] = ruleToFunction[propagationRules[rule]];
// Properties on member level that we treat specially

@@ -72,0 +68,0 @@ const memberPropagationRules = {

{
"name": "@sap/cds-compiler",
"version": "4.8.0",
"version": "4.9.0",
"description": "CDS (Core Data Services) compiler and backends",

@@ -5,0 +5,0 @@ "homepage": "https://cap.cloud.sap/",

@@ -5,2 +5,3 @@ {

"anno-duplicate-unrelated-layer",
"anno-missing-rewrite",
"check-proper-type-of",

@@ -7,0 +8,0 @@ "def-duplicate-autoexposed",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc