Socket
Socket
Sign inDemoInstall

@sap/cds-compiler

Package Overview
Dependencies
Maintainers
3
Versions
106
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 1.20.3 to 1.21.1

lib/api/main.js

10

bin/cdsc.js

@@ -11,2 +11,5 @@ #!/usr/bin/env node

// For recursive *.cds expansion, use
// cdsc $(find . -name '*.cds' -type f)
'use strict';

@@ -42,7 +45,2 @@

let cmdLine = optionProcessor.processCmdLine(process.argv);
// Deal with '--old-csn' explicitly, overrides newCsn!
if(cmdLine.options.oldCsn) {
cmdLine.options.newCsn = false;
delete cmdLine.options.oldCsn;
}
// Deal with '--version' explicitly

@@ -326,3 +324,3 @@ if (cmdLine.options.version) {

options.forHana.associations = options.toCsn.associations;
writeToFileOrDisplay(options.out, name + '_raw.txt', util.inspect(reveal(translateAssocsToJoins(xsn, options), options.rawOutput), false, null), true);
writeToFileOrDisplay(options.out, name + '_raw.txt', util.inspect(reveal(translateAssocsToJoins(xsn), options.rawOutput), false, null), true);
} else {

@@ -329,0 +327,0 @@ writeToFileOrDisplay(options.out, name + '_raw.txt', util.inspect(reveal(xsn, options.rawOutput), false, null), true);

2

bin/cdshi.js

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

if (cat !== 'ref' || chars[ tok.start ] !== '$')
chars[ tok.start ] = categoryChars[ cat ] || cat.charAt();
chars[ tok.start ] = categoryChars[ cat ] || cat.charAt(0);
if (tok.stop > tok.start) // stop in ANTLR at last char, not behind

@@ -37,0 +37,0 @@ chars[ tok.start+1 ] = '_';

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

let fname = path.resolve( '', file );
compiler.compile( [file], '', { attachValidNames: true, lintMode: true, betaMode: true } , { [fname]: src } )
compiler.compile( [file], '', { attachValidNames: true, lintMode: true, beta: true } , { [fname]: src } )
.then( ident, ident );

@@ -93,3 +93,3 @@ }

let fname = path.resolve( '', file );
compiler.compile( [file], '', { lintMode: true, betaMode: true } , { [fname]: buf } )
compiler.compile( [file], '', { lintMode: true, beta: true } , { [fname]: buf } )
.then( select, () => true );

@@ -146,3 +146,3 @@ return true;

for (const prop in node) {
found = (inspect[prop] || inspect[prop.charAt()] || pathitem)( node[prop], node ) || found;
found = (inspect[prop] || inspect[prop.charAt(0)] || pathitem)( node[prop], node ) || found;
if (found === true)

@@ -158,3 +158,3 @@ return found;

let fname = path.resolve( '', file );
compiler.compile( [file], '', { lintMode: true, betaMode: true } , { [fname]: buf } )
compiler.compile( [file], '', { lintMode: true, beta: true } , { [fname]: buf } )
.then( display, display );

@@ -178,6 +178,6 @@ return true;

if (typeof symbol === 'string') {
if (n.length > 3 && n.charAt() === "'" && n.charAt(1) === symbol)
if (n.length > 3 && n.charAt(0) === "'" && n.charAt(1) === symbol)
console.log( n.slice( 2, -1 ), 'symbolCont' );
}
else if (n.charAt() === "'") {
else if (n.charAt(0) === "'") {
if (symbol)

@@ -184,0 +184,0 @@ console.log( n.slice( 1, -1 ), 'symbol' );

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

const { transformForHanaWithCsn } = require('./transform/forHanaNew');
const { compact, compactSorted } = require('./json/compactor')
const { compactModel, sortCsn } = require('./json/to-csn')

@@ -21,5 +20,3 @@ const { toCdsSource, toCdsSourceCsn } = require('./render/toCdl');

const { setProp } = require('./base/model');
var { CompilationError, sortMessages } = require('./base/messages');
const { optionProcessor } = require('./optionProcessor')
const { translateAssocsToJoinsCSN } = require('./transform/translateAssocsToJoins');

@@ -98,3 +95,3 @@ // Transform an augmented CSN 'model' into HANA-compatible CDS source.

result._augmentedCsn = forHanaAugmented;
result.csn = options.newCsn === false ? compactSorted(forHanaAugmented) : compactModel(forHanaAugmented);
result.csn = compactModel(forHanaAugmented);
if(options.testMode)

@@ -127,16 +124,6 @@ result.csn = sortCsn(result.csn);

// Backward compatibility for old naming modes
// FIXME: Remove after a few releases
const { warning, signal } = alerts(csn);
if (options.toHana.names == 'flat') {
signal(warning`Option "{ toHana.names: 'flat' }" is deprecated, use "{ toHana.names: 'plain' }" instead`);
options.toHana.names = 'plain';
}
else if (options.toHana.names == 'deep') {
signal(warning`Option "{ toHana.names: 'deep' }" is deprecated, use "{ toHana.names: 'quoted' }" instead`);
options.toHana.names = 'quoted';
}
const { warning, signal } = alerts(csn, options);
// Verify options
optionProcessor.verifyOptions(options, 'toHana').map(complaint => signal(warning`${complaint}`));
optionProcessor.verifyOptions(options, 'toHana', true).map(complaint => signal(warning`${complaint}`));

@@ -149,4 +136,6 @@ // Special case: For naming variant 'hdbcds' in combination with 'toHana' (and only there!), 'forHana'

options = mergeOptions(options, { forHana: { dialect: 'hana' } }, { forHana : options.toHana });
// Prepare model for HANA (transferring the options to forHana, and setting 'dialect' to 'hana', because 'toHana' is only used for that)
let forHanaCsn = transformForHanaWithCsn(csn, mergeOptions(options, { forHana: { dialect: 'hana' } }, { forHana : options.toHana } ));
let forHanaCsn = transformForHanaWithCsn(csn, options);

@@ -159,3 +148,2 @@ // Assemble result

setProp(sorted, "options", forHanaCsn.options);
//const sorted = forHanaCsn;
result.hdbcds = toCdsSourceCsn(sorted, options);

@@ -272,5 +260,3 @@ } else {

if (options.toOdata.csn) {
// TODO: make compactSortedJson the default
result.csn = options.newCsn === false ? (options.testMode ? compactSorted(forOdataAugmented) : compact(forOdataAugmented))
: compactModel(forOdataAugmented);
result.csn = compactModel(forOdataAugmented);
result._augmentedCsn = forOdataAugmented;

@@ -316,4 +302,2 @@ }

function toOdataWithCsn(csn, options) {
const { error, warning, signal } = alerts(csn);
// In case of API usage the options are in the 'options' argument

@@ -336,15 +320,6 @@ // put the OData specific options under the 'options.toOdata' wrapper

// Backward compatibility for old naming modes
// FIXME: Remove after a few releases
if (options.toOdata.names == 'flat') {
signal(warning`Option "{ toOdata.names: 'flat' }" is deprecated, use "{ toOdata.names: 'plain' }" instead`);
options.toOdata.names = 'plain';
}
else if (options.toOdata.names == 'deep') {
signal(warning`Option "{ toOdata.names: 'deep' }" is deprecated, use "{ toOdata.names: 'quoted' }" instead`);
options.toOdata.names = 'deep';
}
const { error, warning, signal } = alerts(csn, options);
// Verify options
optionProcessor.verifyOptions(options, 'toOdata').map(complaint => signal(warning`${complaint}`));
optionProcessor.verifyOptions(options, 'toOdata', true).map(complaint => signal(warning`${complaint}`));

@@ -356,3 +331,3 @@ // Prepare model for ODATA processing

services: Object.create(null),
messages: csn.messages,
messages: forOdataCSN.messages,
}

@@ -403,2 +378,14 @@ if (options.toOdata.csn) {

// Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
// using 'options'
function preparedCsnToEdmxAll(csn, options) {
// Merge options with those from model
options = mergeOptions(csn.options, options);
let edmx = csn2edmAll(csn, options);
for(const service in edmx){
edmx[service] = edmx[service].toXML('all');
}
return edmx;
}
// Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)

@@ -413,2 +400,14 @@ // using 'options'

// Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
// using 'options'
function preparedCsnToEdmAll(csn, options) {
// Merge options with those from model, override OData version as edm json is always v4
options = mergeOptions(csn.options, options, { toOdata : { version : 'v4' }});
let edmj = csn2edmAll(csn, options);
for(const service in edmj){
edmj[service] = edmj[service].toJSON();
}
return edmj;
}
// ----------- toCdl -----------

@@ -432,4 +431,7 @@

function toCdl(model, options) {
const { warning, signal } = alerts(model);
options = handleToCdlOptions(model, options);
return toCdsSource(model, options);
}
function handleToCdlOptions(model, options, silent=false){
// In case of API usage the options are in the 'options' argument

@@ -444,7 +446,17 @@ // put the OData specific options under the 'options.toCdl' wrapper

options = mergeOptions({ toCdl : true }, model.options, options);
const { warning, signal } = alerts(model, options);
// Verify options
optionProcessor.verifyOptions(options, 'toCdl').map(complaint => signal(warning`${complaint}`));
return toCdsSource(model, options);
optionProcessor.verifyOptions(options, 'toCdl', silent).map(complaint => signal(warning`${complaint}`));
return options;
}
function toCdlWithCsn(csn, options){
options = handleToCdlOptions(csn, options, true);
const result = toCdsSourceCsn(csn, options);
return { result, options };
}
// ----------- toSwagger -----------

@@ -625,3 +637,3 @@

result._augmentedCsn = forSqlAugmented;
result.csn = options.newCsn === false ? compactSorted(forSqlAugmented) : compactModel(forSqlAugmented);
result.csn = compactModel(forSqlAugmented);
}

@@ -657,8 +669,6 @@

function toSqlWithCsn(model, options) {
const { warning, error, signal } = alerts(model);
// when toSql is invoked via the CLI - toSql options are under model.options
// ensure the desired format of the user option
if (model.options && model.options.toSql &&(model.options.toSql.user || model.options.toSql.locale)) {
transforUserOption(model.options.toSql);
transformUserOption(model.options.toSql);
}

@@ -676,3 +686,3 @@

if (options && (options.toSql.user || options.toSql.locale)){
transforUserOption(options.toSql);
transformUserOption(options.toSql);
}

@@ -688,15 +698,6 @@

// Backward compatibility for old naming modes
// FIXME: Remove after a few releases
if (options.toSql.names == 'flat') {
signal(warning`Option "{ toSql.names: 'flat' }" is deprecated, use "{ toSql.names: 'plain' }" instead`);
options.toSql.names = 'plain';
}
else if (options.toSql.names == 'deep') {
signal(warning`Option "{ toSql.names: 'deep' }" is deprecated, use "{ toSql.names: 'quoted' }" instead`);
options.toSql.names = 'quoted';
}
const { warning, error, signal } = alerts(model, options);
// Verify options
optionProcessor.verifyOptions(options, 'toSql').map(complaint => signal(warning`${complaint}`));
optionProcessor.verifyOptions(options, 'toSql', true).map(complaint => signal(warning`${complaint}`));

@@ -745,4 +746,3 @@ // FIXME: Currently, '--to-sql' implies transformation for HANA (transferring the options to forHana)

if (options.toSql.csn) {
result._augmentedCsn = options.testMode ? sortCsn(forSqlCsn) : forSqlCsn;
result.csn = options.newCsn === false || options.testMode? sortCsn(forSqlCsn) : forSqlCsn;
result.csn = options.testMode ? sortCsn(forSqlCsn) : forSqlCsn;
}

@@ -759,3 +759,3 @@

// "id" and/or "locale" prop(s)
function transforUserOption(options) {
function transformUserOption(options) {
// move the user option value under user.id if specified as a string

@@ -861,4 +861,2 @@ if (options.user && typeof options.user === 'string' || options.user instanceof String) {

// testMode : if true, the result is extra-stable for automated tests (sorted, no 'version')
// newCsn : if false, CSN is returned in the old format '0.1.0'
// (default is the new format '1.0')
// toCsn.flavor : if 'gensrc', the result CSN is only suitable for use as a source, e.g. for combination with

@@ -872,5 +870,3 @@ // additional extend/annotate statements, but not for consumption by clients or backends

function toCsn(model, options) {
const { error, warning, signal } = alerts(model);
// Can't have an optional wrapper here because 'testMode' and 'newCsn' are global options
const { warning, signal } = alerts(model);
// In case of API usage the options are in the 'options' argument

@@ -889,16 +885,3 @@ // put the OData specific options under the 'options.toCsn' wrapper

if (options.toCsn.gensrc && !options.newCsn === false) {
signal(error`CSN in "gensrc" flavor can only be generated as new-style CSN (option "newCsn" required)`);
throw new CompilationError(sortMessages(model.messages), model);
}
let csn = options.newCsn === false ? (options.testMode ? compactSorted(model) : compact(model))
: compactModel(model);
if(options.toCsn.associations === "joins"){
options.forHana = {};
options.forHana.associations = options.toCsn.associations;
csn = translateAssocsToJoinsCSN(csn, options);
}
return csn;
return compactModel(model);
}

@@ -952,4 +935,7 @@

preparedCsnToEdmx,
preparedCsnToEdmxAll,
preparedCsnToEdm,
preparedCsnToEdmAll,
toCdl,
toCdlWithCsn,
toSwagger,

@@ -956,0 +942,0 @@ toSql,

@@ -23,3 +23,4 @@ // Implementation of alerts

var locale = process.env.LANG || '';
var language = locale.match(/[a-z]+/);
var languageMatches = locale.match(/[a-z]+/);
var language = (languageMatches && languageMatches.length > 0 && languageMatches[0]) || "";

@@ -100,8 +101,8 @@ // load predefined alerts

* exception, the corresponding error is thrown.
*
* @param {any} msg Message text
* @param {any} location Location information
* @param {any} severity severity: Info, Warning, Error
*
* @param {any} msg Message text
* @param {any} [location] Location information
* @param {string} [severity] severity: Info, Warning, Error
* @param {string} [message_id=''] Message ID
* @param {any} context Further context information
* @param {any} [context] Further context information
* @returns {Boolean} true, if no 'Error' alert was handled.

@@ -108,0 +109,0 @@ */

@@ -9,3 +9,3 @@ const { CompileMessage } = require('../base/messages');

location = searchForLocation(semanticLocation);
}

@@ -22,3 +22,3 @@ return new CompileMessage(location, msg, severity ? severity : msg._severity, message_id, beautifySemanticLocation(semanticLocation), context);

currentThing = currentThing[step];
if(currentThing && currentThing.$location){

@@ -28,3 +28,3 @@ last_location = currentThing.$location;

}
return last_location;

@@ -38,7 +38,7 @@ }

}
if(semanticLocation[0] !== 'definitions'){
throw new Error('Semantic locations must start with "definitions", found: ' + semanticLocation[0]);
}
if(semanticLocation.length == 1){

@@ -64,3 +64,3 @@ throw new Error('Semantic locations must at least point to an artifact!');

}
function quoted( name ) {

@@ -67,0 +67,0 @@ return (name) ? '"' + name.replace( /"/g, '""' ) + '"' : '<?>'; // sync ";

@@ -23,2 +23,8 @@ // Functions and classes for syntax messages

const standardTexts = {
'syntax-csn-expected-object': 'Expected object for property $(PROP)',
'syntax-csn-expected-column': 'Expected object or string \'*\' for property $(PROP)',
'syntax-csn-expected-natnum': 'Expected non-negative number for property $(PROP)',
'syntax-csn-expected-cardinality': 'Expected non-negative number or string \'*\' for property $(PROP)',
'syntax-csn-expected-reference': 'Expected non-empty string or object for property $(PROP)',
'syntax-csn-expected-args': 'Expected array or object for property $(PROP)',
'syntax-anno-after-struct': 'Avoid annotation assignments after structure definitions',

@@ -76,14 +82,32 @@ 'syntax-anno-after-enum': 'Avoid annotation assignments after enum definitions',

return loc;
return (!loc.end || loc.$weak)
? `${filename}:${loc.start.line}:${loc.start.column}`
: (loc.start.line == loc.end.line)
? `${filename}:${loc.start.line}:${loc.start.column}-${loc.end.column}`
: `${filename}:${loc.start.line}.${loc.start.column}-${loc.end.line}.${loc.end.column}`;
if (!loc.end || loc.$weak) {
return (loc.start.column)
? `${filename}:${loc.start.line}:${loc.start.column}`
: `${filename}:${loc.start.line}`;
}
else {
return (loc.start.line == loc.end.line)
? `${filename}:${loc.start.line}:${loc.start.column}-${loc.end.column}`
: `${filename}:${loc.start.line}.${loc.start.column}-${loc.end.line}.${loc.end.column}`;
}
}
// Class for combined compiler errors. Additional members:
// `errors`: vector of errors (CompileMessage and errors from peg.js)
// `model`: the CSN model
// TODO: standard param order
/**
* Class for combined compiler errors. Additional members:
* `errors`: vector of errors (CompileMessage and errors from peg.js)
* `model`: the CSN model
* TODO: standard param order
* @class CompilationError
* @extends {Error}
*/
class CompilationError extends Error {
/**
* Creates an instance of CompilationError.
* @param {array} errs vector of errors (CompileMessage and errors from peg.js)
* @param {object} model the CSN model
* @param {string} [text] Text of the error
* @param {any} args Any args to pass to the super constructor
*
* @memberOf CompilationError
*/
constructor(errs, model, text, ...args) {

@@ -115,5 +139,5 @@ super( text || 'CDS compilation failed\n' + errs.map( m => m.toString() ).join('\n'),

* @param {string} [severity='Error'] Severity: Debug, Info, Warning, Error
* @param {any} id The ID of the message - visible as property messageId
* @param {any} home
* @param {any} context Some further context information, if needed
* @param {string} [id] The ID of the message - visible as property messageId
* @param {any} [home]
* @param {any} [context] Some further context information, if needed
*

@@ -143,6 +167,8 @@ * @memberOf CompileMessage

return loc;
let location = { filename: loc.file, start: { line: loc.line, column: loc.col } };
if (loc.endLine)
location.end = { line: loc.endLine, column: loc.endCol };
else
let location = {
filename: loc.file,
start: { line: loc.line, column: loc.col || 0 },
end: { line: loc.endLine || loc.line, column: loc.endCol || loc.col || 0 },
};
if (!loc.endLine)
location.$weak = true;

@@ -179,3 +205,3 @@ return location;

// If those do not exist, define a non-enumerable property `messages` in `model`.
function getMessageFunction( model, options = model.options || {} ) {
function getMessageFunction( model, options = model.options || {}, transform ) {
let messages = options.messages || model.messages ||

@@ -195,3 +221,3 @@ Object.defineProperty( model, 'messages',

? params
: messageText( texts || standardTexts[id], params );
: messageText( texts || standardTexts[id], params, transform );
let msg = new CompileMessage( location, text, s, id,

@@ -216,2 +242,5 @@ (typeof home === 'string' ? home : homeName(home)) );

id: quoted,
prop: n => "'" + n + "'",
prev_prop: n => "'" + n + "'",
// msg: m => m,
file: s => "'" + s.replace( /'/g, "''" ) + "'", // sync ;

@@ -221,4 +250,4 @@ };

function transformAnno( anno ) {
return (anno.charAt() === '@') ? quoted( anno ) : quoted( '@' + anno );
// if (anno.charAt() === '@')
return (anno.charAt(0) === '@') ? quoted( anno ) : quoted( '@' + anno );
// if (anno.charAt(0) === '@')
// anno = anno.slice(1);

@@ -276,3 +305,3 @@ // return (!anno || /[^A-Za-z_0-9.]/.test(anno)) ? msgName( '@' + anno ) : '@' + anno;

function messageText( texts, params ) {
function messageText( texts, params, transform ) {
if (typeof texts === 'string')

@@ -282,3 +311,3 @@ texts = { std: texts };

for (let p in params) {
let t = paramsTransform[p];
let t = transform && transform[p] || paramsTransform[p];
args[p] = (t) ? t( params[p], args, params, texts ) : params[p];

@@ -291,3 +320,3 @@ }

function replaceInString( text, params ) {
let pattern = /\$\(([A-Z]+)\)/g;
let pattern = /\$\(([A-Z_]+)\)/g;
let parts = [];

@@ -343,16 +372,15 @@ let start = 0;

// Returns a context (code) string that is human readable (similar to rust's compiler)
//
// Example:
// |
// 3 | num * nu
// | ^^
//
/**
* Returns a context (code) string that is human readable (similar to rust's compiler)
*
* Example Output:
* |
* 3 | num * nu
* | ^^
*
* @param {string[]} sourceLines The source code split up into lines, e.g. by `src.split('\n')`
* @param {Object} err Error object containing all details like line, message, etc.
*/
function messageContext(sourceLines, err) {
if (!err.location || !(err.location instanceof Object)) {
return "";
}
const loc = normalizeLocation(err.location);
if (!loc.start) {

@@ -362,17 +390,35 @@ return "";

const startLine = loc.start.line;
if (!sourceLines[startLine - 1]) {
// Lines are 1-based, we need 0-based ones for arrays
const startLine = loc.start.line - 1;
const endLine = loc.end ? loc.end.line - 1 : startLine;
// check that source lines exists
if (!sourceLines[startLine] || !sourceLines[endLine]) {
return "";
}
const digits = loc.end ? String(loc.end.line).length : String(loc.start.line).length;
const digits = String(endLine + 1).length;
const severity = err.severity || 'Error';
const indent = ' '.repeat(2 + digits);
const endColumn = loc.end ? loc.end.column : loc.start.column + 1;
/** Only print N lines even if the error spans more lines. */
const maxLine = Math.min((startLine + 2), endLine);
let msg = "";
msg += indent + '| ' + '\n';
msg += ' ' + startLine + ' | ' + sourceLines[startLine - 1].replace('\t', ' ') + '\n';
msg += indent + '| ' + term.asSeverity(severity, ' '.repeat(loc.start.column - 1).padEnd(endColumn - 1, '^')) + '\n';
let msg = indent + '| ' + '\n';
// print source line(s)
for (let line = startLine; line <= maxLine; line++) {
msg += ' ' + String(line + 1).padStart(digits, ' ') + ' | ' + sourceLines[line].replace('\t', ' ') + '\n';
}
if (startLine === endLine) {
// highlight only for one-line location
msg += indent + '| ' + term.asSeverity(severity, ' '.repeat(Math.max(0, loc.start.column - 1)).padEnd(Math.max(0, endColumn - 1), '^')) + '\n';
} else if (maxLine != endLine) {
// error spans more lines which we don't print
msg += indent + '| ...' + '\n';
} else {
msg += indent + '| ' + '\n';
}
return msg;

@@ -486,4 +532,5 @@ }

CompilationError,
normalizeLocation,
translatePathLocations
}

@@ -227,3 +227,3 @@ 'use strict'

command: undefined,
options: { newCsn: true },
options: { },
unknownOptions: [],

@@ -373,12 +373,9 @@ args: [],

// Return an array of complaints (possibly empty)
function verifyOptions(options, command = undefined) {
function verifyOptions(options, command = undefined, silent = false) {
let result = [];
let opts;
if(options.betaMode && !options.testMode) {
if(options.betaMode && !options.testMode && !silent) {
result.push('Option --beta-mode was used. This option should not be used in productive scenarios!')
}
if (options && options.newCsn === false) {
result.push(`Option --old-csn was used. This option is deprecated and should not be used in productive scenarios!`)
}

@@ -415,3 +412,3 @@ if(options) {

// Don't report commands in top-level options
if (command || !optionProcessor.commands[camelName]) {
if ((command || !optionProcessor.commands[camelName]) && !silent) {
error = `Unknown option "${command ? command + '.' : ''}${camelName}"`;

@@ -418,0 +415,0 @@ }

@@ -216,10 +216,20 @@ 'use strict';

}
let parameters = node.type._artifact? node.type._artifact.parameters : [];
let parameters = (node.type._artifact && node.type._artifact.parameters) || [];
let type = node.type._artifact;
let absolute = type && type.name && type.name.absolute;
for (let name in parameters) {
let param = parameters[name];
if (!node[param] && absolute != "cds.hana.ST_POINT" && absolute != "cds.hana.ST_GEOMETRY")
signal(error`Actual value for type parameter '${param}' missing in reference to type '${absolute}'`,
node.type.location );
// does this type has actual type facets?
let hasTypeFacets = !!parameters.reduce((a,p) => {
a |= node[p] !== undefined;
return a;
}, false);
// Are all type factes provided?
if(absolute !== 'cds.Decimal' || hasTypeFacets) {
for (let name in parameters) {
let param = parameters[name];
if (!node[param] && !['cds.hana.ST_POINT', 'cds.hana.ST_GEOMETRY'].includes(absolute))
signal(error`Actual value for type parameter '${param}' missing in reference to type '${absolute}'`,
node.type.location );
}
}

@@ -234,4 +244,6 @@ switch (absolute) {

case "cds.Decimal": {
checkTypeParamValue(node, 'precision', 'positiveInteger', {max: 38});
checkTypeParamValue(node, 'scale', 'positiveInteger', {max: node.precision && node.precision.val});
if(hasTypeFacets) {
checkTypeParamValue(node, 'precision', 'positiveInteger', {max: 38});
checkTypeParamValue(node, 'scale', 'positiveInteger', {max: node.precision && node.precision.val});
}
break;

@@ -238,0 +250,0 @@ }

@@ -9,5 +9,5 @@ 'use strict';

* Check wether the supplied argument is a virtual element
*
*
* TO CLARIFY: do we want the "no virtual element" check for virtual elements/columns, too?
*
*
* @param {any} arg Argument to check (part of an expression)

@@ -22,6 +22,6 @@ * @returns {Boolean}

* Check a token-stream expression for semantic validity
*
*
* @param {any} xpr The expression to check
* @param {any} model The model
* @returns {undefined}
* @returns {void}
*/

@@ -40,10 +40,10 @@ function checkTokenStreamExpression(xpr, model){

}
}
}
/**
* Check a tree-like expression for semantic validity
*
*
* @param {any} xpr The expression to check
* @param {any} model The model
* @returns {undefined}
* @returns {void}
*/

@@ -78,3 +78,3 @@ function checkTreeLikeExpression(xpr, model){

* Return true if 'xpr' is backlink-like expression (a comparison of "$self" with an assoc)
*
*
* @param {any} xpr The expression to check

@@ -102,7 +102,7 @@ * @returns {Boolean}

/**
* Check an expression (or condition) for semantic validity
*
* Check an expression (or condition) for semantic validity
*
* @param {any} xpr The expression to check
* @param {any} model The model
* @returns {undefined}
* @returns {void}
*/

@@ -109,0 +109,0 @@ function checkExpression(xpr, model) {

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

callKindSpecificCheck(member, currentService);
});
});
});

@@ -112,3 +112,3 @@

// exception: they can live in cds.foundation
// Only an INFO during compile time,
// Only an INFO during compile time,
// reclassified to errors in the backends

@@ -145,3 +145,3 @@ checkNotEmptyOrOnlyVirtualElems(art, model);

if (source(entity)) { // projection
checkProjection(entity, model);
checkProjection(entity);
}

@@ -148,0 +148,0 @@ }

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

},
orderBy: { test: isArray(), requires: [ 'value' ], optional: [ 'sort', 'nulls', '_$queryNode' ] },
orderBy: { test: isArray(), requires: [ 'value' ], optional: [ 'location', 'sort', 'nulls', '_$queryNode' ] },
sort: { test: locationVal( isString ), enum: [ 'asc', 'desc' ] },

@@ -357,3 +357,3 @@ nulls: { test: locationVal( isString ), enum: [ 'first', 'last' ] },

optional: [
'path', 'id', 'quoted', // TODO: req path, opt id for main, req id for member
'path', 'id', 'quoted', 'variant', // TODO: req path, opt id for main, req id for member
'$mixin', // TODO: delete, use kind = 'mixin'

@@ -366,2 +366,3 @@ '_artifact', '$inferred',

absolute: { test: isString },
variant: { test: TODO }, // TODO: not set in CDL parser, only in annotationAssignment
element: { test: TODO }, // TODO: { test: isString },

@@ -474,3 +475,3 @@ action: { test: isString },

function assertProp( node, parent, prop, extraSpec, noPropertyTest ) {
let spec = extraSpec || schema[prop] || schema[prop.charAt()];
let spec = extraSpec || schema[prop] || schema[prop.charAt(0)];
if (!spec)

@@ -481,3 +482,3 @@ throw new Error( `Property '${ prop }' has not been specified`);

if (!noPropertyTest) {
const char = prop.charAt();
const char = prop.charAt(0);
const parser = ('parser' in spec) ? spec.parser : char !== '_' && char !== '$';

@@ -537,3 +538,3 @@ if (stageParser && !parser)

const opt = (optional instanceof Array)
? optional.includes( n ) || optional.includes( n.charAt() )
? optional.includes( n ) || optional.includes( n.charAt(0) )
: optional( n, spec );

@@ -548,3 +549,3 @@ if (!(opt || requires.includes( n ) || n === '$extra'))

function thoseWithKind( prop, spec ) {
const those = spec.schema && spec.schema[prop] || schema[prop] || schema[prop.charAt()];
const those = spec.schema && spec.schema[prop] || schema[prop] || schema[prop.charAt(0)];
return those && those.kind;

@@ -551,0 +552,0 @@ }

@@ -150,3 +150,3 @@ // Compiler functions and utilities shared across all phases

function rejectNonTarget( art ) {
return (options.betaMode && // TODO: delete 'abstract', proper kind: 'aspect'
return ((options.betaMode || options.beta) && // TODO: delete 'abstract', proper kind: 'aspect'
(art.kind === 'type' || art.kind === 'entity' && art.abstract && art.abstract.val))

@@ -346,3 +346,3 @@ ? rejectNonStruct( art )

// TODO: store magic variable in lower case (nicer for code completion)
return (name === 'self' || name.charAt() === '$') ? name : name.toUpperCase();
return (name === 'self' || name.charAt(0) === '$') ? name : name.toUpperCase();
}

@@ -349,0 +349,0 @@

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

<Annotations Target="..."> where Target is the full name of the target
There is one exception (Schema), see below
There is one exception (Schema), see below

@@ -345,3 +345,3 @@ carrier = service

if (action.kind === 'function') {
let mapType = (p) => (p.type.startsWith('cds.') && !p.type.startsWith('cds.foundation.')) ?
let mapType = (p) => (p.type.startsWith('cds.') && !p.type.startsWith('cds.foundation.')) ?
edmUtils.mapCdsToEdmType(p, signal, error, false /*is only called for v4*/) : p.type;

@@ -369,3 +369,3 @@ for (let n in action.params) {

if(carrier['@cds.api.ignore'] || (carrier['@cds.odata.'+options.version+'.ignore'] && options.betaMode)) {
if(!edmUtils.isEdmPropertyRendered(carrier, options)) {
return;

@@ -411,10 +411,15 @@ }

let appliesTest = null; // the condition to decide between std and alt
let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
let testToStandardEdmTarget = ()=>true; // if true, assign to standard Edm Target
let stdName = edmTargetName;
let alternativeEdmTargetName = null;
let hasAlternativeCarrier = true; // is the alternative annotation target available in the EDM?
let hasAlternativeCarrier = false; // is the alternative annotation target available in the EDM?
if (carrier.kind === 'entity' || carrier.kind === 'view') {
// if annotated object is an entity, annotation goes to the EntityType,
// except if AppliesTo contains EntitySet but not EntityType, then annotation goes to EntitySet
appliesTest = (x => x.match(/EntitySet/) && !x.match(/EntityType/));
// except if AppliesTo contains Singleton/EntitySet but not EntityType, then annotation goes to EntitySet
if(carrier['@odata.singleton.nullable'])
testToAlternativeEdmTarget = (x => x.includes('Singleton') && !x.includes('EntityType'));
else
testToAlternativeEdmTarget = (x => x.includes('EntitySet') && !x.includes('EntityType'));
// if carrier has an alternate 'entitySetName' use this instead of EdmTargetName

@@ -432,3 +437,3 @@ // (see edmPreprocessor.initializeParameterizedEntityOrView(), where parameterized artifacts

// except if AppliesTo contains Schema but not EntityContainer, then annotation goes to Schema
appliesTest = (x => x.match(/Schema/) && !x.match(/EntityContainer/));
testToAlternativeEdmTarget = (x => x.includes('Schema') && !x.includes('EntityContainer'));
stdName = edmTargetName + '.EntityContainer';

@@ -438,3 +443,39 @@ alternativeEdmTargetName = edmTargetName;

}
//element => decide if navprop or normal property
else if(!carrier.kind) {
// if appliesTo is undefined, return true
if(carrier.target) {
testToStandardEdmTarget = (x=> x ? x.includes('NavigationProperty') : true);
}
else {
testToStandardEdmTarget = (x=> x ? x.includes('Property') : true);
}
}
/* all AppliesTo entries:
"Action",
"ActionImport",
"Annotation",
"Collection",
"ComplexType",
"EntityContainer",
"EntitySet",
"EntityType",
"Function",
"FunctionImport",
"Include",
"NavigationProperty",
"Parameter",
"Property",
"PropertyValue",
"Record",
"Reference",
"ReturnType",
"Schema",
"Singleton",
"Term",
"TypeDefinition"
*/
// result objects that holds all the annotation objects to be created

@@ -446,3 +487,3 @@ let newAnnosStd = new Edm.Annotations(v, stdName); // used in closure

return function(annotation, appliesTo) {
if (appliesTest && appliesTo && appliesTest(appliesTo)) {
if (testToAlternativeEdmTarget && appliesTo && testToAlternativeEdmTarget(appliesTo)) {
if (carrier.kind === 'service') {

@@ -464,3 +505,3 @@ if (isV2()) {

}
else {
else if(testToStandardEdmTarget(appliesTo)) {
newAnnosStd.append(annotation);

@@ -467,0 +508,0 @@ }

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

const { getUtils, cloneCsn } = require('../model/csnUtils');
const { isNewCSN } = require('../json/csnVersion');
const { CompilationError } = require('../base/messages');
const { checkCSNVersion } = require('../json/csnVersion');

@@ -36,13 +35,9 @@ /*

const { error, warning, info, signal } = alerts(csn);
if (!isNewCSN(csn, _options)) {
let version = csn.version ? csn.version.csn : csn.$version ? csn.$version : 'not available';
signal(error`CSN Version not supported, version tag: "${version}", options.newCsn: ${_options.newCsn}`);
throw new CompilationError(csn.messages, csn);
}
const { error, warning, signal } = alerts(csn);
checkCSNVersion(csn, _options);
let rc = Object.create(null);
const { isBuiltinType, hasBoolAnnotation } = getUtils(csn);
const { isBuiltinType } = getUtils(csn);
let [services, options] = initializeModel(csn, _options, signal, error, warning, info);
let [services, options] = initializeModel(csn, _options);
const Edm = require('./edm.js')(csn, options);

@@ -182,4 +177,16 @@

{
let entitySet = new Edm.EntitySet(v, { Name: EntitySetName, EntityType: fullQualified(EntityTypeName) }, entityCsn);
let containerEntry;
let singleton = entityCsn['@odata.singleton'];
let hasNullable = entityCsn['@odata.singleton.nullable'] !== undefined &&
entityCsn['@odata.singleton.nullable'] !== null;
if(singleton || ((singleton === undefined || singleton === null) && hasNullable)) {
containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
if(entityCsn['@odata.singleton.nullable'])
containerEntry.Nullable= true;
}
else {
containerEntry = new Edm.EntitySet(v, { Name: EntitySetName, EntityType: fullQualified(EntityTypeName) }, entityCsn);
}
// V4: Create NavigationPropertyBinding in EntitySet

@@ -192,5 +199,5 @@ // if NavigationProperty is not a Containment and if the target is not a containee

). forEach(np =>
entitySet.append(np.createNavigationPropertyBinding(namespace)));
containerEntry.append(np.createNavigationPropertyBinding(namespace)));
Schema._ec.append(entitySet);
Schema._ec.append(containerEntry);
}

@@ -320,3 +327,3 @@

for (let p in actionCsn)
if (p.match(/^@sap./))
if (p.match(/^@sap\./))
functionImport.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : actionCsn[p] });

@@ -383,4 +390,7 @@

}
else if(!hasBoolAnnotation(elementCsn, '@cds.api.ignore', true) &&
!(hasBoolAnnotation(elementCsn, '@cds.odata.'+options.version+'.ignore', true) && options.betaMode))
// render ordinary property if element is NOT ...
// 1) ... annotated @cds.api.ignore, @cds.odata.{v2|v4}.ignore (+betaMode)
// 2) ... annotated @odata.foreignKey4 and odataFormat: structured
else if(edmUtils.isEdmPropertyRendered(elementCsn, options))
{

@@ -529,3 +539,3 @@ // CDXCORE-CDXCORE-173

reuseAssoc = !!forwardAssocCsn._NavigationProperty;
constraints = edmUtils.getReferentialConstraints(forwardAssocCsn, signal, warning);
constraints = edmUtils.getReferentialConstraints(forwardAssocCsn, signal, warning, options.isFlatFormat);
constraints._multiplicity = edmUtils.determineMultiplicity(forwardAssocCsn);

@@ -532,0 +542,0 @@ }

@@ -10,4 +10,5 @@ 'use strict'

const { error, info, signal } = alerts(csn);
const { flattenStructuredElement } = transformUtils.getTransformers(csn, '/');
const { error, info, signal } = alerts(csn, options);
const pathDelimiter = options.isStructFormat ? '/' : '_';
const { flattenStructuredElement } = transformUtils.getTransformers(csn, options, '/');

@@ -287,3 +288,2 @@ const {

}
return json;
}

@@ -441,21 +441,6 @@

class EntitySet extends Node
class Singleton extends Node
{
// virtual
setSapVocabularyAsAttributes(csn)
{
if(csn)
{
for (let p in csn._EntitySetAttributes)
{
if (p.match(/^@sap./))
this.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : csn._EntitySetAttributes[p] } );
}
}
}
toJSONattributes(json)
{
// OASIS ODATA-1231 $Collection=true
json['$Collection']=true;
for (let p in this)

@@ -487,4 +472,28 @@ {

class EntitySet extends Singleton
{
// virtual
setSapVocabularyAsAttributes(csn)
{
if(csn)
{
for (let p in csn._EntitySetAttributes)
{
if (p.match(/^@sap\./))
this.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : csn._EntitySetAttributes[p] } );
}
}
}
toJSONattributes(json)
{
// OASIS ODATA-1231 $Collection=true
json['$Collection']=true;
return super.toJSONattributes(json);
}
}
class Key extends Node
{
// keys is an array of [name] or [name, alias]
constructor(v, keys)

@@ -495,3 +504,3 @@ {

{
keys.forEach(k => this.append(new PropertyRef(v, k)));
keys.forEach(k => this.append(new PropertyRef(v, ...k)));
}

@@ -698,7 +707,7 @@ }

// why is flattenStructuredElements not working on simple types?
if (isStructured(k._csn.type, csn) || (k._csn.type && getFinalTypeDef(k._csn.type, csn).elements)) {
let flatElems = flattenStructuredElement(k._csn, k.Name);
keyPaths.push(...Object.keys(flatElems));
if (isStructured(k._csn.type) || (k._csn.type && getFinalTypeDef(k._csn.type).elements)) {
// flatten the struct paths to (a/b/c) and create an alias __ET_a_b_c for each path
keyPaths.push(...Object.keys(flattenStructuredElement(k._csn, k.Name)).map(k => [k, '__'+this.Name+k.split(pathDelimiter).join('')]));
} else {
keyPaths.push(k.Name);
keyPaths.push([k.Name]);
}

@@ -870,3 +879,5 @@ });

{
constructor(v, Name) { super(v, { Name }); }
constructor(v, Name, Alias) {
super(v, (Alias) ? { Name, Alias } : { Name });
}
}

@@ -894,5 +905,9 @@

let json = Object.create(null);
for (let p in this)
json[p[0] == '@' ? p : '$' + p] = this[p]
return json;
for (let p in this) {
if (!(p == 'Nullable' && this[p] == false))
{
json[p[0] == '@' ? p : '$' + p] = this[p]
}
}
return this.toJSONattributes(json);
}

@@ -925,3 +940,2 @@ }

delete this.Nullable;
}

@@ -933,3 +947,2 @@ let partner = (csn._partnerCsn.length > 0) ? csn._partnerCsn[0] : csn._constraints._originAssocCsn;

/*

@@ -1032,3 +1045,3 @@ 1) If this navigation property belongs to an EntityType for a parameterized entity

edmUtils.forAll(this._csn._constraints.constraints,
c => this.append(new ReferentialConstraint(this._v, { Property: c[0], ReferencedProperty: c[1] } ) ) );
c => this.append(new ReferentialConstraint(this._v, { Property: c[0].join(pathDelimiter), ReferencedProperty: c[1].join(pathDelimiter) } ) ) );
}

@@ -1337,4 +1350,4 @@ }

edmUtils.forAll(c, cv => {
node._d.append(new PropertyRef(v, cv[0]));
node._p.append(new PropertyRef(v, cv[1]));
node._d.append(new PropertyRef(v, cv[0].join(pathDelimiter)));
node._p.append(new PropertyRef(v, cv[1].join(pathDelimiter)));
});

@@ -1352,2 +1365,3 @@ return node;

EntitySet,
Singleton,
TypeDefinition,

@@ -1354,0 +1368,0 @@ EnumType,

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

forAll(csn.definitions, (a, n) => {
a.name = n;
setProp (a, 'name', n);
});

@@ -52,3 +52,3 @@

forAll(a.params, (p,n) => {
p.name = n;
setProp (p, 'name', n);
})

@@ -101,5 +101,6 @@ });

}
// TODO: Remove betaMode if finalized
// in V4 tag all compositions to be containments
// TOOD: remove betaMode once odataContainment option is finalized
if(options.betaMode &&
options.odataContainment &&
options.isV4() &&

@@ -259,3 +260,4 @@ isComposition(element) &&

// Attach name and Name (Name is used in function ForeignKey4(assoc))
element.name = element.Name = elementName;
element.Name = elementName;
setProp(element, 'name', elementName)
setProp(element, '_parent', struct);

@@ -337,3 +339,3 @@

//forward annotations from managed association element to its foreign keys
if(element.keys) {
if(element.keys && options.isFlatFormat) {
for(let fk of element.keys) {

@@ -353,3 +355,3 @@ forAll(element, (attr, attrName) => {

if (element._ignore) return;
setProp(element, '_constraints', getReferentialConstraints(element, signal, warning));
setProp(element, '_constraints', getReferentialConstraints(element, signal, warning, options.isFlatFormat));

@@ -410,7 +412,7 @@ // only in V2 we must set the target cardinality of the backlink to the forward:

if(myServiceName !== whatsMyServiceName(element._target.name)) {
if(options.isStructured() && options.isV4() && options.odataProxies) {
if(options.isStructured && options.isV4() && options.odataProxies) {
// search for eventually existing proxy
let proxy = element._target.$proxies.filter(p => p.name.startsWith(myServiceName + '.'))[0];
if(!proxy) {
let name = myServiceName + '.' + element._target.name.split('.').join('_') + '_proxy';
let name = myServiceName + '.' + element._target.name.split('.').join('_') + '_proxy_0';
proxy = { name, kind: 'entity', $proxy: true, elements: Object.create(null) };

@@ -514,2 +516,9 @@ let hasKeys = false;

}
//'@Capabilities.ReadRestrictions.Readable'
if(containee['@Capabilities.ReadRestrictions.Readable'] !== undefined) {
navRestr.RestrictedProperties[0].ReadRestrictions =
{ 'Readable': containee['@Capabilities.ReadRestrictions.Readable'] };
delete containee['@Capabilities.ReadRestrictions.Readable'];
hasRestrictions = true;
}
if(hasRestrictions)

@@ -516,0 +525,0 @@ container['@Capabilities.NavigationRestrictions'] = navRestr;

@@ -12,4 +12,10 @@ 'use strict';

const options = Object.assign({ version: 'v4'}, _options);
if (options.toOdata && options.toOdata.version)
options.version = options.toOdata.version;
if (options.toOdata) {
if(options.toOdata.version)
options.version = options.toOdata.version;
if(options.toOdata.odataFormat)
options.odataFormat = options.toOdata.odataFormat;
if(options.toOdata.odataContainment)
options.odataContainment = options.toOdata.odataFormat;
}

@@ -20,2 +26,4 @@ const v2 = options.version.match(/v2/i) != undefined;

options.v = [v2, v4];
options.isStructFormat = options.odataFormat && options.odataFormat === 'structured';
options.isFlatFormat = !options.isStructFormat;

@@ -28,5 +36,2 @@ if(options.v.filter(v=>v).length != 1)

options.isStructured = function() { return options.toOdata.odataFormat === 'structured' }
options.isFlat = function() { return options.toOdata.odataFormat === 'flat' }
return options;

@@ -38,2 +43,11 @@ }

// render ordinary property if element is NOT ...
// 1) ... annotated @cds.api.ignore, @cds.odata.{v2|v4}.ignore (+betaMode)
// 2) ... annotated @odata.foreignKey4 and odataFormat: structured
function isEdmPropertyRendered(elementCsn, options) {
return(!elementCsn['@cds.api.ignore']) &&
!(elementCsn['@cds.odata.'+options.version+'.ignore'] && options.betaMode) &&
!(elementCsn['@odata.foreignKey4'] && options.isStructFormat)
}
// returns intersection of two arrays

@@ -89,3 +103,3 @@ function intersect(a,b)

// Return true if the association 'assoc' has cardinality 'to-many'
// Return true if the association 'assoc' has cardinality 'to-many'
function isToMany(assoc) {

@@ -133,3 +147,3 @@ if (!assoc.cardinality) {

function getReferentialConstraints(assocCsn, signal, warning)
function getReferentialConstraints(assocCsn, signal, warning, isFlatFormat)
{

@@ -147,3 +161,3 @@ let result = { constraints: Object.create(null), selfs: [], termCount: 0 };

/* example for originalTarget:
entity E (with parameters) {
entity E (with parameters) {
... keys and all the stuff ...

@@ -173,6 +187,7 @@ toE: association to E;

if(!assocCsn._target.isParamEntity && originAssocCsn.key) {
if(originAssocCsn.keys) {
if(originAssocCsn.keys && isFlatFormat) {
for(let fk of originAssocCsn.keys) {
let c = [ fk.ref[0], fk.$generatedFieldName ];
result.constraints[c] = c;
const c = [ [ fk.ref[0] ], [ fk.$generatedFieldName ] ];
const key = c.join(',');
result.constraints[key] = c;
}

@@ -230,4 +245,4 @@ }

c => {
let fk = dependentEntity.elements[c[0]];
let pk = principalEntity.$keys[c[1]];
let fk = dependentEntity.elements[c[0][0]];
let pk = principalEntity.$keys[c[1][0]];
return !(pk && fk && !(pk['@cds.api.ignore'] || fk['@cds.api.ignore']));

@@ -246,11 +261,13 @@ },

// FIXME: If path is something structured, perform a path resolution (or use augmented CSN)
if(!assocCsn._target.isParamEntity && assocCsn.keys) {
for(let fk of assocCsn.keys) {
let realFk = assocCsn._parent.elements[fk.$generatedFieldName];
let pk = assocCsn._target.elements[fk.ref[0]];
if(pk && pk.key && !(pk['@cds.api.ignore'] || realFk['@cds.api.ignore']))
{
let c = [ fk.$generatedFieldName, fk.ref[0] ];
result.constraints[c] = c;
if(isFlatFormat) {
let realFk = assocCsn._parent.elements[fk.$generatedFieldName];
let pk = assocCsn._target.elements[fk.ref[0]];
if(pk && pk.key && !(pk['@cds.api.ignore'] || realFk['@cds.api.ignore']))
{
const c = [ [ fk.$generatedFieldName ], [ fk.ref[0] ] ];
const key = c.join(',');
result.constraints[key] = c;
}
}

@@ -305,2 +322,9 @@ }

// if exactly one operand starts with the prefix then this is potentially a constraint
// strip of prefix '$self's
if(lhs[0] === '$self' && lhs.length > 1)
lhs = lhs.slice(1);
if(rhs[0] === '$self' && rhs.length > 1)
rhs = rhs.slice(1);
if((lhs[0] === assocCsn.name && rhs[0] !== assocCsn.name) ||

@@ -311,14 +335,17 @@ (lhs[0] !== assocCsn.name && rhs[0] === assocCsn.name))

//backlink [ self, assocName ]
let c;
if(lhs[0] === assocCsn.name)
c = [rhs[0], lhs[1]];
c = [rhs, lhs.slice(1)];
else
c = [lhs[0], rhs[1]];
c = [lhs, rhs.slice(1)];
// do we have a $self id?
// if so, store partner in selfs array
if(c[0] === '$self')
result.selfs.push(c[1]);
else
result.constraints[c] = c;
if(c[0][0] === '$self' && c[0].length === 1) {
result.selfs.push(c[1][0]);
} else {
const key = c.join(',');
result.constraints[key] = c;
}
}

@@ -454,3 +481,3 @@ }

{
if (edmType == 'Edm.Date')
if (edmType == 'Edm.Date')
edmType = 'Edm.DateTime';

@@ -503,2 +530,3 @@ if (edmType == 'Edm.TimeOfDay')

validateOptions,
isEdmPropertyRendered,
intersect,

@@ -505,0 +533,0 @@ foreach,

@@ -549,3 +549,3 @@ let W = require("./walker");

augmentDefinition(PATH[0], NODE);
let PROTO = Object.getPrototypeOf(NODE);

@@ -552,0 +552,0 @@ W.forEach(NODE, (key,node) => {

@@ -465,2 +465,3 @@ // contains query relevant augmentor functions

return undefined;
/** @type any */
let ret;

@@ -467,0 +468,0 @@ if(literal===undefined)

@@ -202,6 +202,6 @@ let W = require("./walker");

elementKind(node, path);
if(node.items)
augmentItems(path.concat("items"),node.items)
function elementName(name, node, path) {

@@ -213,3 +213,3 @@ node.name = {

}
function elementKind(node, path) {

@@ -223,3 +223,3 @@ let kind = "element";

}
}

@@ -384,3 +384,3 @@

}
let transformers = {

@@ -387,0 +387,0 @@ newValue,

@@ -265,4 +265,6 @@ // Transform augmented CSN into compact "official" CSN

}
if (model.version)
newModel.version = model.version;
// hard code the final old style compact CSN version
if (!options.testMode) {
newModel.version = { csn: "0.1.0" };
}

@@ -269,0 +271,0 @@ let PERSIST_COMPACTED_CSN = "PERSIST_COMPACTED_CSN" in process.env;

// function converts IN-PLACE CSN object to XSN object without any CSN validation
function csn2xsn( csn, options = {} ) {
let augmentor3 = require("./augmentor3.js");
let xsn = augmentor3.augment(csn); // in-place
xsn.$frontend = 'json';
let compileSources = require('../main').compileSources;
return compileSources( { '<stdin>.csn': csn }, options );
return compileSources( { '<stdin>.csn': xsn }, options );
}
module.exports = csn2xsn
module.exports = csn2xsn
// csn version functions
// The CSN file format version produced by this compiler
// (Note that all 0.x.x versions are floating targets, i.e. the format is frequently
// changed without notice. The versions only signal certain combinations of "flavors", see below)
// (Note: the SQL name mapping mode is not reflected in the content of the csn, the version only
// signals which default name mapping a backend has to use)
// Historic versions:
// 0.0.1 : Used by HANA CDS for its CSN output (incomplete, not well defined, quite different from CDX ...)
// 0.0.2 : CDX in the initial versions with old-style CSN, default for SQL name mapping is 'quoted'
// 0.0.99 : Like 0.0.2, but with new-style CSN
// Versions that are currently produced by compiler:
// 0.1.0 : Like 0.0.2, default for SQL name mapping is 'plain'
// 0.1.99 : Like 0.1.0, but with new-style CSN
// 0.2 : same as 0.1.99, but with new top-level properties: $version, meta
const newCSNVersions = ["0.1.99","0.2","0.2.0","1.0"];

@@ -8,3 +22,3 @@ // checks if new-csn is requested vie the options of already specified in the CSN

function isNewCSN(csn, options) {
if( (options && options.newCsn === false) ||
if( (options && options.newCsn === false) ||
(csn.version && !newCSNVersions.includes(csn.version.csn)) ||

@@ -14,8 +28,24 @@ (csn.$version && !newCSNVersions.includes(csn.$version)))

return false;
}
}
return true;
}
function checkCSNVersion(csn, options) {
// the new transformer works only with new CSN
const alerts = require('../base/alerts');
const { CompilationError } = require('../base/messages');
const { error, signal } = alerts(csn);
if (!isNewCSN(csn, options)) {
let errStr = 'CSN Version not supported, version tag: "';
errStr += (csn.version && csn.version.csn ? csn.version.csn : (csn.$version ? csn.$version : 'not available')) + '"';
errStr += (options.newCsn !== undefined) ? ', options.newCsn: ' + options.newCsn : '';
signal(error`${errStr}`);
throw new CompilationError(csn.messages, csn);
}
}
module.exports = {
isNewCSN
isNewCSN,
checkCSNVersion
}
/**
* This module is used for parsing csn as json and returned as augmented CSN.
* @param source json formatted string representation of the csn
* @param file name if the originating file - used for error reporting
* @param options augmentor options
* @param {string} source json formatted string representation of the csn
* @param {string} filename name if the originating file - used for error reporting
* @param {Object} options augmentor options
*/
function fromJson(source, filename, options) {

@@ -9,0 +8,0 @@ let jlParser = require("./parse")

@@ -7,3 +7,3 @@ // Transform augmented CSN into compact "official" CSN

const { locationString } = require('../base/messages');
const { assignAll } = require('../model/csnUtils');
const { assignAll } = require('../model/csnUtils'); // TODO: remove if old CSN frontend is gone

@@ -118,3 +118,3 @@ const creator = 'CDS Compiler v' + require('../../package.json').version;

kind: ['annotate','extend'],
op: ['join','func','xpr'],
op: ['join','func','xpr'], // TODO: 'func','xpr' into 'quantifier'? TODO: 'global'(scope)?
quantifier: ['some','any','distinct', 'ref','_links','_art','_scope', 'param', 'val','literal', 'SELECT','SET'], // 'all' explicitly listed

@@ -147,4 +147,4 @@ type: ['_type'],

const typeProperties = [ // just for `cast` in select items
'type', 'length', 'precision', 'scale', 'srid', 'items', 'target', 'elements', 'enum'
const typeProperties = [ // sync with definition in from-csn.js
'type', 'length', 'precision', 'scale', 'srid', 'items', 'target', 'elements', 'enum',
];

@@ -178,3 +178,3 @@

const val = csn[n];
if (!val || typeof val !== 'object' || n.charAt() === '@' || csnDirectValues.includes(n)) {
if (!val || typeof val !== 'object' || n.charAt(0) === '@' || csnDirectValues.includes(n)) {
r[n] = val;

@@ -213,4 +213,2 @@ }

set( 'messages', csn, model );
if (model.version)
csn.version = model.version; // TODO remove with CSN version 1.1
if (!options.testMode) {

@@ -319,3 +317,5 @@ csn.meta = Object.assign( {}, model.meta, { creator } );

function elements( dict, csn, node ) {
if (csn.from || csn_gensrc && (node.query || node.type)) // with SELECT or inferred elements with gensrc
if (csn.from || csn_gensrc && (node.query || node.type))
// no 'elements' with SELECT or inferred elements with gensrc;
// hidden 'elements' will be set in query()
return undefined;

@@ -338,3 +338,5 @@ if (node.kind !== 'event')

// transformer (= value) takes care to exclude $inferred annotation assignments
let sub = transformer( val, csn, node, prop );
let sub = transformer( val );
// As value() just has one value, so we do not provide ( val, csn, node, prop )
// which would be more robust, but makes some JS checks unhappy
if (sub !== undefined)

@@ -355,3 +357,3 @@ csn[prop] = sub;

function location( loc, csn, xsn ) {
if (xsn.kind && xsn.kind.charAt() !== '$' && xsn.kind !== 'query' &&
if (xsn.kind && xsn.kind.charAt(0) !== '$' && xsn.kind !== 'query' &&
(!xsn.$inferred || !xsn._main)) {

@@ -635,4 +637,15 @@ // Also include $location for elements in queries (if not via '*')

const elems = node.elements;
if (elems && node._main && node._main.$queries && node !== node._main.$queries[0])
setHidden( select, 'elements', elements( elems, select, node ) );
if (elems && node._main && node !== node._main._leadingQuery && csn_gensrc !== true) {
// Set hidden 'elements' for csnRefs.js. In select-item subqueries,
// csn_gensrc might have been set to 'column' and must be set to the
// original value 'false' - otherwise no element appears.
let saved_gensrc = csn_gensrc;
try {
csn_gensrc = false;
setHidden( select, 'elements', insertOrderDict( elems ) );
}
finally {
csn_gensrc = saved_gensrc;
}
}
return addLocation( node.location, select );

@@ -650,7 +663,10 @@ }

let args = node.args;
// binary -> n-ary - the while loop should be done in parser (toCdl is
// binary -> n-ary - TODO CDL: the while loop should be done in parser (toCdl is
// currently not prepared)
while (args[0] && args[0].op && args[0].op.val === node.op.val &&
!args[0].all === !node.all && args[0].args)
args = [ ...args[0].args, ...args.slice(1) ]
const block = node._leadingQuery && node._leadingQuery._block;
if (!block || block.$frontend !== 'json') {
while (args[0] && args[0].op && args[0].op.val === node.op.val &&
!args[0].all === !node.all && args[0].args)
args = [ ...args[0].args, ...args.slice(1) ]
}
if (node.op.val === 'unionAll') // TODO grammar: set DISTINCT - quantifier: 'all'|'distinct'

@@ -732,3 +748,3 @@ csn.all = true;

try {
csn_gensrc = true;
csn_gensrc = csn_gensrc || 'column';
set( 'key', col, elem );

@@ -788,3 +804,3 @@ addExplicitAs( assignAll( col, expression(elem.value) ),

function addExplicitAs( node, name, implicit ) {
if (name && (!name.calculated && !name.$inferred || implicit && implicit(name.id) ))
if (name && (!name.calculated && !name.$inferred || !node.ref || implicit && implicit(name.id) ))
node.as = name.id;

@@ -796,5 +812,5 @@ return node;

let path = ref && ref.path;
return path && function( id ) {
let last = path[ path.length-1 ];
return (last && last.id) !== id;
return function( id ) {
let last = path && path[ path.length-1 ];
return !last || last.id !== id;
};

@@ -806,4 +822,4 @@ }

return 0;
let oa = propertyOrder[a] || propertyOrder[a.charAt()] || 9999;
let ob = propertyOrder[b] || propertyOrder[b.charAt()] || 9999;
let oa = propertyOrder[a] || propertyOrder[a.charAt(0)] || 9999;
let ob = propertyOrder[b] || propertyOrder[b.charAt(0)] || 9999;
return oa - ob || (a < b ? -1 : 1);

@@ -810,0 +826,0 @@ }

@@ -99,3 +99,3 @@ /**

* @param {walkWithPathCallback} callback function called for each node: callback(isNode,path,node)
* @param check - optional callback function which returns true if the walk should continue
* @param {(newPath: any, obj: Object) => boolean} [check] - optional callback function which returns true if the walk should continue
*/

@@ -377,3 +377,3 @@ function walkWithPath(node, callback, check) {

* @param {getNextElements} getNextElements callback to obtain the next node to walk, passing the current node as parameter
* @param {build} callback called on each walked node passing that node and returns the resulting product of the operation on this node
* @param {(any) => any} build called on each walked node passing that node and returns the resulting product of the operation on this node
*/

@@ -380,0 +380,0 @@ function walkAndBuild(root, getNextElements, build) {

@@ -81,3 +81,3 @@ // Wrapper around generated ANTLR parser

const rules = {
cdl: { func: 'start', returns: 'source', frontend: 'cdl' },
cdl: { func: 'start', returns: 'source', $frontend: 'cdl' },
query: { func: 'queryEOF', returns: 'query' },

@@ -133,4 +133,4 @@ expr: { func: 'conditionEOF', returns: 'cond' } // yes, condition

ast.options = options;
if (rulespec.frontend)
ast.$frontend = rulespec.frontend;
if (rulespec.$frontend)
ast.$frontend = rulespec.$frontend;

@@ -137,0 +137,0 @@ if (parser.messages) {

@@ -68,3 +68,2 @@ // Error strategy with special handling for (non-reserved) keywords

antlr4_error.DefaultErrorStrategy.call( this, ...args );
return this;
}

@@ -71,0 +70,0 @@ const super1 = antlr4_error.DefaultErrorStrategy.prototype;

@@ -50,3 +50,2 @@ // Generic ANTLR parser class with AST-building functions

hanaFlavorOnly,
betaModeOnly,
csnParseOnly,

@@ -118,12 +117,2 @@ noAssignmentInSameLine,

function betaModeOnly( text, ...tokens ) {
if (!text || this.options.betaMode)
return;
if (typeof text !== 'string') {
tokens = [ text, ...tokens ];
text = tokens.map( t => t.text.toUpperCase() ).join(' ') + ' is only supported with --beta-mode';
}
this.message( null, this.tokenLocation( tokens[0], tokens[ tokens.length-1 ] ), text );
}
// Use the following function for language constructs which we (currently) do

@@ -271,4 +260,8 @@ // not really compile, just use to produce a CSN for functions parseToCqn() and

this.message( 'syntax-empty-ident', token, {},
'Error', 'Quoted identifier must contain at least one character' );
'Error', 'Delimited identifier must contain at least one character' );
}
else if (!/^[a-zA-Z0-9---_$%&/@#=?|<>^:]+(\.[a-zA-Z0-9---_$%&/@#=?|<>^:]+)*$/.test( id )) {
this.message( 'syntax-suspicious-ident', token, {}, 'Warning',
'Suspicious delimited identifier - strings are delimited by single quotes' );
}
return { id, quoted: true, location: this.tokenLocation( token ) };

@@ -422,7 +415,13 @@ }

// Assign all non-empty (undefined, null, {}, []) properties in argument
// `props` and argument `annos` as property `annotationAssignments` to `target`
// and return it. Hack: if argument `annos` is exactly `true`, return
// `Object.assign( target, props )`. ANTLR tokens are replaced by their
// locations.
/** Assign all non-empty (undefined, null, {}, []) properties in argument
* `props` and argument `annos` as property `annotationAssignments` to `target`
* and return it. Hack: if argument `annos` is exactly `true`, return
* `Object.assign( target, props )`. ANTLR tokens are replaced by their
* locations.
*
* @param {any} target
* @param {any[]|true} [annos=[]]
* @param {any} [props]
* @param {any} [location]
*/
function assignProps( target, annos = [], props, location ) {

@@ -429,0 +428,0 @@ if (annos === true)

@@ -17,3 +17,5 @@ // Main entry point for the Research Vanilla CDS Compiler

const backends = require('./backends');
const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
// The compiler version (taken from package.json)

@@ -24,23 +26,2 @@ function version() {

// The CSN file format version produced by this compiler
// (Note that all 0.x.x versions are floating targets, i.e. the format is frequently
// changed without notice. The versions only signal certain combinations of "flavors", see below)
// (Note: the SQL name mapping mode is not reflected in the content of the csn, the version only
// signals which default name mapping a backend has to use)
// Historic versions:
// 0.0.1 : Used by HANA CDS for its CSN output (incomplete, not well defined, quite different from CDX ...)
// 0.0.2 : CDX in the initial versions with old-style CSN, default for SQL name mapping is 'quoted'
// 0.0.99 : Like 0.0.2, but with new-style CSN
// Versions that are currently produced by compiler:
// 0.1.0 : Like 0.0.2, default for SQL name mapping is 'plain'
// 0.1.99 : Like 0.1.0, but with new-style CSN
// 0.2 : same as 0.1.99, but with new top-level properties: $version, meta
// TODO: move csn versioning in to-csn.js
function csnVersion( options ) {
// Merge default options
options = mergeOptions(backends.getDefaultBackendOptions(), options);
// Old-style CSN vs new-style CSN
return options.newCsn === false ? '0.1.0' : '1.0';
}
var { CompilationError, messageString, messageStringMultiline, messageContext, handleMessages, hasErrors, getMessageFunction }

@@ -62,3 +43,2 @@ = require('./base/messages');

const emdx2csn = require('./edm/annotations/edmx2csnNew'); // translate edmx annotations into csn
const { mergeOptions } = require('../lib/model/modelUtils');

@@ -80,5 +60,8 @@ const path = require('path');

else if (['.json', '.csn'].includes(ext)) {
if (!options.oldCsnFrontend) {
return require('./json/from-csn').parse( source, filename, options );
}
if(options.stdJsonParser) {
let csn = JSON.parse(source);
let augmentor3 = require("./json/augmentor3");
let augmentor3 = require('./json/augmentor3');
let xsn = augmentor3.augment(csn); // in-place

@@ -174,3 +157,3 @@ xsn.$frontend = 'json';

return collect();
return compileDo( model, a.fileContentDict );
return compileDo( model );
});

@@ -343,3 +326,11 @@

return new Promise( function (fulfill, reject) {
moduleResolve( dep.module, opts, function (err, res) {
// If the global 'cds.home' is set, read modules starting with '@sap/cds/' from there.
// TODO: re-think:
// * what is wrong for a JAVA installation to set a link...
// * preferred to a local installation? Not the node-way...
// * a global? The Umbrella could pass it as an option...
let path = (global.cds && global.cds.home && dep.module.startsWith( '@sap/cds/' ))
? global.cds.home + dep.module.slice(8) // path.resolve() does not work - huh?
: dep.module;
moduleResolve( path, opts, function (err, res) {
// console.log('RESOLVE', dep, res, err)

@@ -432,6 +423,6 @@ if (err)

// Argument `sourcesDict` is a dictionary (it could actually be a ordinary object)
// mapping filenames to either source texts (string) or an AST-like augmented
// CSNs. It could also be a simple string, which is then considered to be the
// source text of a file named `<stdin>.cds`.
// Argument `sourcesDict` could also be the output of collectSources(), see above.
// mapping filenames to either source texts (string) or XSN objects (AST-like
// augmented CSNs). It could also be a simple string, which is then considered
// to be the source text of a file named `<stdin>.cds`. Argument `sourcesDict`
// could also be the output of collectSources(), see above.
//

@@ -460,7 +451,4 @@ // See function `compile` for the meaning of the argument `options`. If there

}
else { // source is a CSN object
let augmentor3 = require("./json/augmentor3.js");
let xsn = augmentor3.augment(source); // in-place
xsn.$frontend = 'json';
sources[filename] = xsn;
else { // source is a XSN object (CSN/CDL parser output)
sources[filename] = source;
}

@@ -497,3 +485,2 @@ }

if (!options.testMode) {
model.version = versionObject( options );//TODO remove
model.meta = {}; // provide initial central meta object

@@ -543,9 +530,2 @@ }

// Return the 'version' object that should appear in CSNs generated by this compiler.
function versionObject( options ) { // TODO remove
return {
csn: csnVersion( options ),
}
}
// Class for command invocation errors. Additional members:

@@ -557,2 +537,3 @@ // `errors`: vector of errors (file IO or ArgumentError)

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

@@ -617,2 +598,4 @@ }

parseToExpr,
for: { odata },
to: { cdl, sql, hdi, hdbcds, edm, edmx }
};

@@ -130,3 +130,3 @@ // CSN functionality for resolving references

// 3: $magic
if (head.charAt() === '$') {
if (head.charAt(0) === '$') {
if (head === '$self' || head === '$projection') {

@@ -133,0 +133,0 @@ let self = query ? queryOrMain( query, main ) : main;

@@ -226,3 +226,3 @@ 'use strict'

* Add an annotation with absolute name 'absoluteName' (including '@') and string value 'theValue' to 'node'
*
*
* @param {any} absoluteName Name of the annotation, including '@'

@@ -266,11 +266,11 @@ * @param {any} theValue string value of the annotation

* instead (this seems to be intended for handling annotations that start with '@' ?)
*
*
* Regardless of their names, transformers are never applied to dictionary elements.
*
*
* The transformer functions are called with the following signature:
* transformer(value, node, resultNode, key)
*
*
* @param {any} node Node to transform
* @param {any} transformers Object defining transformer functions
* @returns
* @returns {Object}
*/

@@ -311,3 +311,3 @@ function cloneWithTransformations(node, transformers) {

}
// Resolve to the final type of a type, that means follow type chains, references to other types or

@@ -424,3 +424,3 @@ // elements a.s.o

// Clone each property
let resultValue = clone(node[key], node, resultNode, key);
let resultValue = clone(node[key]);
if (resultValue !== undefined) {

@@ -442,3 +442,3 @@ resultNode[key] = resultValue;

}
return resultNode;

@@ -518,5 +518,5 @@ }

if(node._ignore){
return;
return;
}
if(Array.isArray(node)){

@@ -588,3 +588,3 @@ for (let i = 0; i < node.length; i++) {

}
function traverseFrom( from, callback, path = [] ) {

@@ -597,3 +597,3 @@ if (from.ref) // ignore

}
}
}
else

@@ -600,0 +600,0 @@ traverseQuery( from, callback, path ); // sub query in FROM

@@ -170,2 +170,19 @@ 'use strict'

// Add an annotation with absolute name 'absoluteName' (including '@') and enum value 'theValue' to 'node'
function addEnumAnnotationTo(absoluteName, theValue, node) {
// Sanity check
if (!absoluteName.startsWith('@')) {
throw Error('Annotation name should start with "@": ' + absoluteName);
}
// Assemble the annotation
node[absoluteName] = {
name: {
absolute: absoluteName.substring(1),
location: node.location, // inherit location from main element
},
symbol: { id: theValue },
literal: 'enum',
location: node.location, // inherit location from main element
};
}
// Add an annotation with absolute name 'absoluteName' (including '@') and path ref 'theValue' to 'node'

@@ -455,2 +472,3 @@ function addRefAnnotationTo(absoluteName, theValue, node) {

addBoolAnnotationTo,
addEnumAnnotationTo,
addRefAnnotationTo,

@@ -457,0 +475,0 @@ renameAnnotation,

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

function dictionary( node ) {
return reveal( node, '__proto__' );
return reveal( node, '__proto__' );
}

@@ -186,0 +186,0 @@

@@ -27,5 +27,4 @@ const { createOptionProcessor } = require('./base/optionProcessorHelper');

.option(' --old-transformers')
.option(' --new-csn')
.option(' --old-csn')
.option(' --std-json-parser')
.option(' --old-csn-frontend')
.option(' --long-autoexposed')

@@ -35,4 +34,2 @@ .option(' --hana-flavor')

.option(' --test-mode')
.option('--precision <prec>')
.option('--scale <scale>')
.option('--length <length>')

@@ -70,4 +67,2 @@ .help(`

Type options
--precision <prec> Default precision for 'cds.Decimal'
--scale <scale> Default scale for 'cds.Decimal'
--length <length> Default 'length' for 'cds.String'

@@ -87,5 +82,4 @@

--old-transformers Use the old transformers that work on XSN instead of CSN
--new-csn Produce CSN version 1.0 format (default)
--old-csn Produce CSN version 0.1 format (deprecated, overrides --new-csn)
--std-json-parser Use standard JSON parser for CSN parsing
--old-csn-frontend Use old CSN frontend
--std-json-parser Use standard JSON parser for CSN parsing with old CSN frontend
--hana-flavor Compile with backward compatibility for HANA CDS (incomplete)

@@ -148,2 +142,3 @@ --parse-only Stop compilation after parsing and write result to <stdout>

.option(' --combined')
.option(' --odata-containment')
.option('-c, --csn')

@@ -171,2 +166,3 @@ .option('-f, --odata-format <format>', ['flat', 'structured'])

structured : (V4 only) Render structured metadata
--odata-containment Generate Containment Navigation Properties for compositions (V4 only)
-n, --names <style> Annotate artifacts and elements with "@cds.persistence.name", which is

@@ -278,3 +274,2 @@ the corresponding database name (see "--names" for "toHana or "toSql")

.option('-f, --flavor <flavor>', ['client', 'gensrc'])
.option('-a, --associations <proc>', ['assocs', 'joins'])
.help(`

@@ -293,5 +288,2 @@ Usage: cdsc toCsn [options] <file...>

backends
-a, --associations <proc> Processing of associations:
assocs : (default) Keep associations in HANA CDS as far as possible
joins : Transform associations to joins
`);

@@ -298,0 +290,0 @@

@@ -20,3 +20,3 @@ /**

* Check for duplicate artifacts or elements
*
*
* @class DuplicateChecker

@@ -34,3 +34,3 @@ */

* Initialize the state of the checker.
*
*
* @memberOf DuplicateChecker

@@ -45,9 +45,9 @@ */

* Add an artifact to the "seen"-list
*
* @param {any} name
* @param {any} location
*
*
* @param {any} name
* @param {any} location
*
* @memberOf DuplicateChecker
*/
addArtifact( name, location, modelName ){
addArtifact( name, location, modelName ){
const dbname = asDBName(name);

@@ -63,7 +63,7 @@ this.currentArtifact = { name, location, elements: {}, modelName };

* Add an element to the "seen"-list
*
* @param {any} name
* @param {any} location
* @returns
*
*
* @param {any} name
* @param {any} location
* @returns
*
* @memberOf DuplicateChecker

@@ -85,3 +85,3 @@ */

* No more artifacts need to be processed, check for duplicates and re-init the object.
*
*
* @memberOf DuplicateChecker

@@ -88,0 +88,0 @@ */

"use strict";
const { CompilationError, hasErrors, sortMessages } = require('../base/messages');
const { mergeOptions, getTopLevelArtifactNameOf, getParentNameOf, getLastPartOf,

@@ -12,4 +11,5 @@ getLastPartOfRef, getParentNamesOf } = require('../model/modelUtils');

const DuplicateChecker = require('./DuplicateChecker');
const { cloneCsn } = require('../model/csnUtils');
const { setProp } = require('../base/model');
const { checkCSNVersion } = require('../json/csnVersion');
const { handleMessages } = require('../base/messages');

@@ -36,5 +36,5 @@

// FIXME: This comment no longer tells the whole truth
function toCdsSourceCsn(model, options) {
function toCdsSourceCsn(csn, options) {
// Merge options (arguments first, then model options)
options = mergeOptions(model.options, options);
options = mergeOptions(csn.options, options);
let plainNames = options.forHana && options.forHana.names == 'plain';

@@ -44,6 +44,8 @@ let hdbcdsNames = options.forHana && options.forHana.names == 'hdbcds';

// Skip compactModel if already using CSN
const csn = cloneCsn(model);
//const csn = cloneCsn(model);
const { signal, warning, error } = alerts(csn);
const { signal, warning, error } = alerts(csn, options);
checkCSNVersion(csn, options);
let result = Object.create(null);

@@ -71,3 +73,3 @@

// This environment is passed down the call hierarchy, for dealing with
// indentation and name resolution issues
// indentation and name resolution issues
let env = createEnv();

@@ -83,3 +85,3 @@ let sourceStr = renderArtifact(artifactName, csn.definitions[artifactName], env); // Must come first because it populates 'env.topLevelAliases'

globalDuplicateChecker && globalDuplicateChecker.check(signal, error); // perform duplicates check
// If there are unapplied 'extend' and 'annotate' statements, render them separately

@@ -94,5 +96,3 @@ // FIXME: Clarify if we should also do this for HANA (probably not?)

// Throw exception in case of errors
if (hasErrors(csn.messages)) {
throw new CompilationError(sortMessages(csn.messages), csn);
}
handleMessages(csn, options);
return result;

@@ -157,2 +157,4 @@

function renderArtifact(artifactName, art, env) {
// FIXME: Correctly build the paths during runtime to give better locations
env.path = ['definitions', artifactName];
// Ignore whole artifacts if toHana says so

@@ -399,3 +401,3 @@ if (art._ignore) {

duplicateChecker && elm && duplicateChecker.addElement(quoteOrUppercaseId(elementName), elm && elm.$location, elementName);
result += env.indent + (elm.virtual ? 'virtual ' : '')
result += env.indent + (elm.virtual ? 'virtual ' : '')
+ (elm.key ? 'key ' : '')

@@ -577,3 +579,3 @@ + ((elm.masked && !elm._ignoreMasked)? 'masked ' : '')

// Return the resulting source string (no trailing LF).
function renderViewColumn(col, env, path) {
function renderViewColumn(col, env) {
// Ignore if toHana says so

@@ -592,5 +594,5 @@ // FIXME: This probably needs to change ...

} else {
// If key is explicitly set in a not-first query of a UNION, issue an error.
// If key is explicitly set in a non-leading query, issue an error.
if(col.key && env.skipKeys){
signal(error`KEY must only be added in the first query of a UNION`, path);
signal(error`KEY must only be added in the leading query`, env.path);
}

@@ -660,2 +662,3 @@ const key = (!env.skipKeys && (col.key || (options.forHana && leaf && env._artifact.elements[leaf] && env._artifact.elements[leaf].key)) ? 'key ' : '');

let result = '';
env.skipKeys = !isLeadingQuery;
// Set operator, like UNION, INTERSECT, ...

@@ -667,6 +670,2 @@ if (query.SET) {

if (query.SET.op) {
if(query.SET.op === 'union'){
// For UNION, don't render "key X as X" in the second SELECT
env.skipKeys = true;
}
// Loop over all other arguments, i.e. for A UNION B UNION C UNION D ...

@@ -676,4 +675,2 @@ for(let i = 1; i < query.SET.args.length; i++){

}
// Reset afterwards
env.skipKeys = false;
}

@@ -704,3 +701,3 @@ result += ')';

} else {
throw new Error(`Unknown query syntax: ${syntax}`);
throw new Error(`Unknown query syntax: ${syntax}`);
}

@@ -721,3 +718,3 @@ if (select.mixin) {

result += ' {\n' +
(select.columns||['*']).map((col, index) => renderViewColumn(col, childEnv, path.concat('SELECT','columns',index)))
(select.columns||['*']).map((col) => renderViewColumn(col, childEnv))
.filter(s => s != '')

@@ -912,5 +909,3 @@ .join(',\n') + '\n';

if (isBuiltinType(elm.type)) {
// cds.Integer => render as Integer (no quotes)
result += elm.type.replace(/^cds\./, '');
result += renderTypeParameters(elm);
result += renderBuiltinType(elm);
} else {

@@ -927,2 +922,11 @@ // Simple absolute name

function renderBuiltinType(elm) {
// cds.Integer => render as Integer (no quotes)
// Map Decimal (w/o Prec/Scale) to cds.DecimalFloat for HANA CDS
if(options.toHana && elm.type === 'cds.Decimal' && elm.scale === undefined && elm.precision === undefined)
return 'DecimalFloat';
else {
return elm.type.replace(/^cds\./, '') + renderTypeParameters(elm);
}
}
// Render the 'enum { ... } part of a type declaration

@@ -1170,3 +1174,3 @@ function renderEnum(enumPart, env) {

// Render (primitive) type parameters of element 'elm', i.e.
// Render (primitive) type parameters of element 'elm', i.e.
// length, precision and scale (even if incomplete), plus any other unknown ones.

@@ -1272,3 +1276,3 @@ function renderTypeParameters(elm /*, env */) {

// fact that 'absName' is used in 'env', so that an appropriate USING can be constructed
// if necessary.
// if necessary.
function renderAbsoluteNamePlain(absName, env) {

@@ -1336,3 +1340,3 @@ // Add using declaration

}
// Depending on the naming style, render the namespace declaration for a top-level artifact 'name'

@@ -1339,0 +1343,0 @@ // if it has a namespace parent. Assume that this is only called for top-level artifacts.

@@ -24,3 +24,3 @@

// FIXME: Currently requires 'options.forHana', because it requires the 'forHana' transformations
// FIXME: Currently requires 'options.forHana', because it requires the 'forHana' transformations
if (!options.forHana) {

@@ -27,0 +27,0 @@ throw new Error('toRenameDdl can currently only be used with HANA preprocessing');

"use strict";
const { CompilationError, hasErrors, sortMessages } = require('../base/messages');
const { getTopLevelArtifactNameOf, getParentNameOf, getParentNamesOf, getLastPartOf, getLastPartOfRef } = require('../model/modelUtils');

@@ -11,4 +10,5 @@ const keywords = require('../base/keywords');

const DuplicateChecker = require("./DuplicateChecker");
const { checkCSNVersion } = require('../json/csnVersion');
const { handleMessages } = require('../base/messages');
// Type mapping from cds type names to DB type names:

@@ -77,3 +77,3 @@ // (in the future, we would introduce an option for the mapping table)

function toSqlDdl(csn, options = csn.options) {
const { error, signal, warning, info } = alerts(csn);
const { error, signal, warning, info } = alerts(csn, options);

@@ -85,2 +85,5 @@ // FIXME: Currently requires 'options.forHana', because it can only render HANA-ish SQL dialect

checkCSNVersion(csn, options);
// Create artificial namespace objects, so that each artifact has parents up to top-level.

@@ -125,5 +128,3 @@ // FIXME: This is actually only necessary to make 'getParentNameOf' work - should be reworked

// Throw exception in case of errors
if (hasErrors(csn.messages)) {
throw new CompilationError(sortMessages(csn.messages), csn);
}
handleMessages(csn, options);

@@ -205,3 +206,3 @@ // Transfer results from hdb-specific dictionaries into 'sql' dictionary in proper order if toSql.src == 'sql'

let result = '';
// Only HANA has row/column tables
// Only HANA has row/column tables
if (options.toSql.dialect == 'hana') {

@@ -257,3 +258,3 @@ if (hanaTc && hanaTc.storeType) {

// Only HANA has indices
// FIXME: Really? We should provide a DB-agnostic way to specify that
// FIXME: Really? We should provide a DB-agnostic way to specify that
if (options.toSql.dialect === 'hana') {

@@ -420,3 +421,3 @@ renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);

// FIXME: Misleading name, should be something like 'renderQueryFrom'. All the query
// parts should probably also be rearranged.
// parts should probably also be rearranged.
// Returns the source as a string.

@@ -763,3 +764,3 @@ function renderViewSource(artifactName, source, env) {

// Render the nullability of an element or parameter (can be unset, true, or false)
// Render the nullability of an element or parameter (can be unset, true, or false)
function renderNullability(obj, treatKeyAsNotNull = false) {

@@ -773,3 +774,3 @@ if (obj.notNull === undefined && !(obj.key && treatKeyAsNotNull)) {

// Render (primitive) type parameters of element 'elm', i.e.
// Render (primitive) type parameters of element 'elm', i.e.
// length, precision and scale (even if incomplete), plus any other unknown ones.

@@ -776,0 +777,0 @@ function renderTypeParameters(elm) {

@@ -87,3 +87,3 @@ const schemaObjects = require('./swaggerSchemaObjects');

// if the user wants some specific order or more complicated path => should specify it with the @Swagger.path annotation
// and have to take care of correct parameters names
// and have to take care of correct parameters names
if (!action['@Swagger.path'] && action.params)

@@ -90,0 +90,0 @@ Object.keys(action.params).forEach(pName => {

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

const { isManagedAssociationElement, isStructuredElement, isAssociation, isAssocOrComposition, isElementWithType,
renameAnnotation, addBoolAnnotationTo, addStringAnnotationTo, addRefAnnotationTo, copyAnnotations,
renameAnnotation, addBoolAnnotationTo, addStringAnnotationTo, addEnumAnnotationTo, addRefAnnotationTo, copyAnnotations,
foreachPath, hasBoolAnnotation, getElementDatabaseNameOf, getArtifactDatabaseNameOf } = require('../model/modelUtils');

@@ -22,3 +22,3 @@ const transformUtils = require('./transformUtils');

// '@odata.foreignKey4'). Propagate along projections accordingly. Names are built using
// <assoc>_<key>, conflicts are checked.
// <assoc>_<key>, conflicts are checked.
// - Complete the 'foreignKeys' property for all managed associations, so that there

@@ -52,3 +52,3 @@ // is always a 'generatedFieldName' for the corresponding generated foreign key field.

extractValidFromToKeyElement, checkAssignment, checkMultipleAssignments } = transformUtils.getTransformers(model, '_');
// semantic checks before flattening

@@ -63,2 +63,4 @@ forEachDefinition(model, artifact => {

validKey.push(...k);
});

@@ -264,4 +266,14 @@ // Check that @cds.valid.from/to/key is only in valid places

// CDXCORE-458
else if (member.kind == 'element' && member.items && options.toOdata.version == 'v2') {
signal(error`"${artifact.name.absolute}.${memberName}": Element must not be an "array of" for OData V2`, member.location);
else if (member.kind == 'element' && member.items) {
if(options.toOdata.version == 'v2') {
signal(error`"${artifact.name.absolute}.${memberName}": Element must not be an "array of" for OData V2`, member.location);
}
else if(['entity', 'view'].includes(artifact.kind)) {
if(member.items.elements && !member.items.type) {
signal(error`"${artifact.name.absolute}.${memberName}": Element must not be an "array of anonymous type"`, member.location);
}
if(!member.items.elements && member.items.type && !member.items.type._artifact.builtin && member.items.type._service != artifact._service) {
signal(error`${artifact.name.absolute}.${memberName}": Array type "${member.items.type._artifact.name.absolute}" is not member of service`, member.location);
}
}
}

@@ -376,3 +388,3 @@

* Check if any warnings were raised in earlier steps that need to be reclassified - i.e. as errors
*
*
* @param {any} model The model

@@ -399,2 +411,71 @@ * @returns {Array} Reclassified messages-Array

// Rename shorthand annotations within artifact or element 'node' according to a builtin
// list.
function renameShorthandAnnotations(node) {
// FIXME: Verify this list - are they all still required? Do we need any more?
const mappings = {
'@label': '@Common.Label',
'@title': '@Common.Label',
'@description': '@Core.Description',
'@ValueList.entity': '@Common.ValueList.entity',
'@ValueList.type': '@Common.ValueList.type',
'@Capabilities.Deletable': '@Capabilities.DeleteRestrictions.Deletable',
'@Capabilities.Insertable': '@Capabilities.InsertRestrictions.Insertable',
'@Capabilities.Updatable': '@Capabilities.UpdateRestrictions.Updatable',
}
let rewriteCapabilities = true;
if(hasBoolAnnotation(node, '@readonly') && hasBoolAnnotation(node, '@insertonly') && ['entity', 'view'].includes(node.kind)) {
rewriteCapabilities = false;
signal(warning`"@readonly" and "@insertonly" cannot be assigned in combination`, node.location);
}
for (let name in node) {
// Rename according to map above
if (mappings[name] != undefined) {
renameAnnotation(node, name, mappings[name]);
}
// Special case: '@important: [true|false]' becomes '@UI.Importance: [#High|#Low]'
if (name == '@important') {
renameAnnotation(node, name, '@UI.Importance');
let annotation = node['@UI.Importance'];
annotation.literal = 'enum';
annotation.symbol = {
// Note that an original '@important' without ': true' shows up as undefined value here!!
id: (annotation.val == undefined || annotation.val == true) ? 'High' : 'Low',
};
// FIXME: Strangely, enum-valued annotations have no value
delete annotation.val;
}
// Special case: '@readonly' becomes a triplet of capability restrictions for entities,
// but '@Core.Immutable' for everything else.
if(rewriteCapabilities) {
if (name == '@readonly') {
if (node.kind == 'entity' || node.kind == 'view') {
addBoolAnnotationTo('@Capabilities.DeleteRestrictions.Deletable', false, node);
addBoolAnnotationTo('@Capabilities.InsertRestrictions.Insertable', false, node);
addBoolAnnotationTo('@Capabilities.UpdateRestrictions.Updatable', false, node);
}
else {
renameAnnotation(node, name, '@Core.Computed');
}
}
// @insertonly is effective on entities/queries only
else if (name == '@insertonly') {
if (node.kind == 'entity' || node.kind == 'view') {
addBoolAnnotationTo('@Capabilities.DeleteRestrictions.Deletable', false, node);
addBoolAnnotationTo('@Capabilities.ReadRestrictions.Readable', false, node);
addBoolAnnotationTo('@Capabilities.UpdateRestrictions.Updatable', false, node);
}
}
}
// Only on element level: translate @mandatory
if(name === '@mandatory' && node.kind === 'element' && node['@Common.FieldControl'] === undefined) {
addEnumAnnotationTo('@Common.FieldControl', 'Mandatory', node);
delete node['@mandatory'];
}
}
}
// Apply checks to all annotations in the model

@@ -488,6 +569,14 @@ // node: artifact/element/action/function/parameter/... that carries the annotations

for (let elemName in artifact.elements) {
let elem = artifact.elements[elemName];
// Make all non-key elements nullable
if (elem.notNull && elem.notNull.val===true && !(elem.key && elem.key.val)) {
delete elem.notNull;
}
}
// Generate the additional elements into the draft-enabled artifact
// key IsActiveEntity : Boolean default true
let isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true);
let isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true, false);
addBoolAnnotationTo('@UI.Hidden', true, isActiveEntity);

@@ -497,3 +586,3 @@ addElement(isActiveEntity, artifact);

// HasActiveEntity : Boolean default false
let hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false);
let hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false, true);
addBoolAnnotationTo('@UI.Hidden', true, hasActiveEntity);

@@ -503,3 +592,3 @@ addElement(hasActiveEntity, artifact);

// HasDraftEntity : Boolean default false;
let hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false);
let hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false, true);
addBoolAnnotationTo('@UI.Hidden', true, hasDraftEntity);

@@ -557,7 +646,2 @@ addElement(hasDraftEntity, artifact);

// Make all non-key elements nullable
if (elem.notNull && elem.notNull.val===true && !(elem.key && elem.key.val)) {
elem.notNull.val = false;
}
// Draft-enable the targets of composition elements (draft nodes), too

@@ -694,5 +778,5 @@ if (elem.target && elem._finalType.type && elem._finalType.type._artifact.name.absolute === 'cds.Composition') {

* Mark elements that are annotated with @odata.on.insert/update with the annotation @Core.Computed.
*
*
* Implements: CDXCORE-62
*
*
* @param {any} node The node to check

@@ -710,3 +794,3 @@ */

//
//
/**

@@ -718,3 +802,3 @@ * Mark association whose target has @cds.odata.valuelist with @Common.ValueList.viaAssociation

*
* Do this only if the association is navigable and the enclosing artifact is
* Do this only if the association is navigable and the enclosing artifact is
* a service member (don't pollute the CSN with unnecessary annotations).

@@ -724,4 +808,2 @@ *

*
* @param {any} artifact The parent artifact
* @param {any} node The node to check
*/

@@ -738,51 +820,2 @@ function addCommonValueListviaAssociation(member) {

// Rename shorthand annotations within artifact or element 'node' according to a builtin
// list.
function renameShorthandAnnotations(node) {
// FIXME: Verify this list - are they all still required? Do we need any more?
const mappings = {
'@label': '@Common.Label',
'@title': '@Common.Label',
'@description': '@Core.Description',
'@ValueList.entity': '@Common.ValueList.entity',
'@ValueList.type': '@Common.ValueList.type',
'@Capabilities.Deletable': '@Capabilities.DeleteRestrictions.Deletable',
'@Capabilities.Insertable': '@Capabilities.InsertRestrictions.Insertable',
'@Capabilities.Updatable': '@Capabilities.UpdateRestrictions.Updatable',
}
for (let name in node) {
// Rename according to map above
if (mappings[name] != undefined) {
renameAnnotation(node, name, mappings[name]);
}
// Special case: '@important: [true|false]' becomes '@UI.Importance: [#High|#Low]'
if (name == '@important') {
renameAnnotation(node, name, '@UI.Importance');
let annotation = node['@UI.Importance'];
annotation.literal = 'enum';
annotation.symbol = {
// Note that an original '@important' without ': true' shows up as undefined value here!!
id: (annotation.val == undefined || annotation.val == true) ? 'High' : 'Low',
};
// FIXME: Strangely, enum-valued annotations have no value
delete annotation.val;
}
// Special case: '@readonly' becomes a triplet of capability restrictions for entities,
// but '@Core.Immutable' for everything else.
if (name == '@readonly') {
if (node.kind == 'entity' || node.kind == 'view') {
addBoolAnnotationTo('@Capabilities.DeleteRestrictions.Deletable', false, node);
addBoolAnnotationTo('@Capabilities.InsertRestrictions.Insertable', false, node);
addBoolAnnotationTo('@Capabilities.UpdateRestrictions.Updatable', false, node);
}
else {
renameAnnotation(node, name, '@Core.Computed');
}
}
}
}
// Return an array of non-abstract service names contained in (augmented or compacted) 'model''

@@ -789,0 +822,0 @@ function getServiceNames(model) {

'use strict';
const alerts = require('../base/alerts');
const { CompilationError, hasErrors, sortMessages } = require('../base/messages');
const { handleMessages } = require('../base/messages');
const { setProp } = require('../base/model');

@@ -9,3 +9,3 @@ const transformUtils = require('./transformUtilsNew');

const { getUtils, cloneCsn, forEachDefinition, forEachMemberRecursively } = require('../model/csnUtils');
const { isNewCSN } = require('../json/csnVersion');
const { checkCSNVersion } = require('../json/csnVersion');

@@ -62,13 +62,11 @@ // Transformation for ODATA. Expects a CSN 'inputModel', processes it for ODATA.

const { error, warning, info, signal } = alerts(csn);
// the new transformer works only with new CSN
if (!isNewCSN(inputModel, options)) {
signal(error`OData transformer does not support the provided CSN version.`);
throw new CompilationError(csn.messages, csn);
}
options = mergeOptions(inputModel.options, options);
setProp(csn, 'options', options);
const { error, warning, info, signal } = alerts(csn, options);
// the new transformer works only with new CSN
checkCSNVersion(csn, options);
const {

@@ -86,3 +84,3 @@ flattenForeignKeys, createForeignKeyElement,

recurseElements,
} = transformUtils.getTransformers(csn, '_');
} = transformUtils.getTransformers(csn, options, '_');

@@ -92,3 +90,2 @@ const {

getCsnDef,
getFinalBaseType,
getFinalType,

@@ -101,2 +98,3 @@ getFinalTypeDef,

isAssociation,
isBuiltinType,
isManagedAssociationElement,

@@ -117,3 +115,3 @@ isStructured,

// (0) Semantic checks before flattening regarding temporal data
// (0) Semantic checks before flattening regarding temporal data and array of
forEachDefinition(csn, (artifact, artifactName, propertyName, path) => {

@@ -129,3 +127,3 @@ // Gather all element names with @cds.valid.from/to/key

// Check that @cds.valid.from/to/key is only in valid places
validFrom.forEach(obj => checkAssignment('@cds.valid.from', obj.element, obj.path, artifact, artifactName));
validFrom.forEach(obj => checkAssignment('@cds.valid.from', obj.element, obj.path, artifact));
validTo.forEach(obj => checkAssignment('@cds.valid.to', obj.element, obj.path, artifact));

@@ -185,4 +183,4 @@ validKey.forEach(obj => checkAssignment('@cds.valid.key', obj.element, obj.path, artifact));

//
// "S.Struct2" should looks just like "S.Struct1" => the "type": "S.Struct1" property has to be removed
if (def.kind === 'type' && def.type && isStructured(getFinalBaseType(def)) && !def.type.ref) {
// "S.Struct2" should looks just like "S.Struct1" => the "type": "S.Struct1" property has to be removed
if (def.kind === 'type' && def.type && !isBuiltinType(def.type) && !def.type.ref) {
// elements are already there -> do not show the type

@@ -206,3 +204,3 @@ delete def.type;

// (1.3) Resolve annotation shorthands for elements, actions, action parameters
renameShorthandAnnotations(member);
renameShorthandAnnotations(member, path);

@@ -217,3 +215,3 @@ // (1.4) check annotations

let elem = def.elements[elemName];
if (isStructured(elem, csn) || (elem.type && getFinalTypeDef(elem.type, csn).elements)) {
if (isStructured(elem) || (elem.type && getFinalTypeDef(elem.type).elements)) {
if (structuredOData) {

@@ -249,3 +247,3 @@ if (!isArtifactInSomeService(defName, services)) return;

// (1.5) Resolve annotation shorthands for entities, types, annotations, ...
renameShorthandAnnotations(def);
renameShorthandAnnotations(def, path);
// (1.6) check annotations

@@ -322,3 +320,3 @@ checkAnnotations(def, ['definitions', defName]);

if (isAssocOrComposition(elem.type)) {
checkForeignKeys(elem, elemName, defName);
checkForeignKeys(elem, elemName, defName, options);
}

@@ -339,6 +337,6 @@

forEachMemberRecursively(def, (member, memberName, propertyName, path) => {
if (isArtifactInSomeService(defName, services)) {
if (isArtifactInSomeService(defName, services) || isLocalizedArtifactInService(defName, services)) {
let service = getServiceOfArtifact(defName, services);
// (4.2) Check associations
if (isAssocOrComposition(member.type, csn)) {
if (isAssocOrComposition(member.type)) {
// Check that exposed associations do not point to non-exposed targets

@@ -349,11 +347,23 @@ checkExposedAssoc(defName, member, memberName, service);

if (def.kind === 'type' && options.toOdata.version == 'v2') {
signal(warning`"${defName}.${memberName}": Structured types must not contain associations for OData V2`);
signal(warning`"${defName}.${memberName}": Structured types must not contain associations for OData V2`, path);
}
}
// (4.3) CDXCORE-458
else if (propertyName === 'elements' && member.items && options.toOdata.version == 'v2') {
signal(error`"${defName}.${memberName}": Element must not be an "array of" for OData V2`, path);
else if (propertyName === 'elements' && member.items) {
if (options.toOdata.version == 'v2') {
signal(error`"${defName}.${memberName}": Element must not be an "array of" for OData V2`, path);
}
else if (['entity', 'view'].includes(def.kind)) {
// array of <anonymous type> is not allowed
// array of T is allowed, if T is in defining service or in namespace 'cds'
if (member.items.elements && !member.items.type) {
signal(error`"${defName}.${memberName}": Element must not be an "array of anonymous type"`, path);
}
if (member.items.type && !member.items.elements &&
!isArtifactInSomeService(member.items.type, ['cds', getServiceName(defName)])) {
signal(error`${defName}.${memberName}": Array type "${member.items.type}" is not member of service`, path);
}
}
}
// (4.4) If the member is an association and the target is annotated with @cds.odata.valuelist,

@@ -483,7 +493,9 @@ // annotate the association with @Common.ValueList.viaAssociation (but only for service member artifacts

if (options.messages) reclassifyWarnings(options);
else if (csn.messages) reclassifyWarnings(csn);
// Throw exception in case of errors
if (hasErrors(csn.messages)) {
csn.messages = reclassifyWarnings(csn);
throw new CompilationError(sortMessages(csn.messages), csn);
}
handleMessages(csn, options);
if (options.messages) setProp(csn, 'messages', options.messages);
return csn;

@@ -504,3 +516,3 @@

// list.
function renameShorthandAnnotations(node) {
function renameShorthandAnnotations(node, path) {
// FIXME: Verify this list - are they all still required? Do we need any more?

@@ -518,2 +530,7 @@ const mappings = {

let rewriteCapabilities = true;
if(node['@readonly'] && node['@insertonly'] && ['entity', 'view'].includes(node.kind)) {
rewriteCapabilities = false;
signal(warning`"@readonly" and "@insertonly" cannot be assigned in combination`, path);
}
for (let name in node) {

@@ -533,11 +550,26 @@ // Rename according to map above

// but '@Core.Immutable' for everything else.
if (name == '@readonly') {
if (node.kind == 'entity' || node.kind == 'view') {
node['@Capabilities.DeleteRestrictions.Deletable'] = false;
node['@Capabilities.InsertRestrictions.Insertable'] = false;
node['@Capabilities.UpdateRestrictions.Updatable'] = false;
} else {
renameAnnotation(node, name, '@Core.Computed');
if(rewriteCapabilities) {
if (name == '@readonly') {
if (node.kind == 'entity' || node.kind == 'view') {
node['@Capabilities.DeleteRestrictions.Deletable'] = false;
node['@Capabilities.InsertRestrictions.Insertable'] = false;
node['@Capabilities.UpdateRestrictions.Updatable'] = false;
} else {
renameAnnotation(node, name, '@Core.Computed');
}
}
// @insertonly is effective on entities/queries only
else if (name == '@insertonly') {
if (node.kind == 'entity' || node.kind == 'view') {
node['@Capabilities.DeleteRestrictions.Deletable'] = false;
node['@Capabilities.ReadRestrictions.Readable'] = false;
node['@Capabilities.UpdateRestrictions.Updatable'] = false;
}
}
}
// Only on element level: translate @mandatory
if (name === '@mandatory' && node.kind === undefined && node['@Common.FieldControl'] === undefined) {
node['@Common.FieldControl'] = { '#': 'Mandatory' };
delete node['@mandatory'];
}
}

@@ -639,3 +671,4 @@ }

flattenedKeyArts[defName] = true;
forEachMemberRecursively(def, (member, memberName) => {
let rootPath =['definitions',defName]
forEachMemberRecursively(def, (member, memberName, prop, subpath) => {
// Generate foreign key elements for managed associations

@@ -646,4 +679,5 @@ if (isManagedAssociationElement(member) && !member._ignore) {

// Generate foreign key elements
for (let key of member.keys) {
let foreignKeyElement = createForeignKeyElement(member, memberName, key, def, defName);
for (let keyIndex in member.keys) {
let key = member.keys[keyIndex];
let foreignKeyElement = createForeignKeyElement(member, memberName, key, def, defName, rootPath.concat(subpath).concat('keys', keyIndex));
toFinalBaseType(foreignKeyElement);

@@ -668,3 +702,3 @@ // Propagate the association's annotations to the foreign key element

// (3.2) Flatten on-conditions in unmanaged associations
if (member.type && isAssocOrComposition(member.type, csn) && member.on) {
if (member.type && isAssocOrComposition(member.type) && member.on) {
flattenOnCond(member, memberName, def.elements, defName);

@@ -748,6 +782,13 @@ }

for (let elemName in artifact.elements) {
let elem = artifact.elements[elemName];
// Make all non-key elements nullable
if (elem.notNull && elem.key !== true) {
delete elem.notNull;
}
}
// Generate the additional elements into the draft-enabled artifact
// key IsActiveEntity : Boolean default true
let isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true);
let isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true, false);
isActiveEntity.IsActiveEntity['@UI.Hidden'] = true;

@@ -757,3 +798,3 @@ addElement(isActiveEntity, artifact);

// HasActiveEntity : Boolean default false
let hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false);
let hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false, true);
hasActiveEntity.HasActiveEntity['@UI.Hidden'] = true;

@@ -763,3 +804,3 @@ addElement(hasActiveEntity, artifact);

// HasDraftEntity : Boolean default false;
let hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false);
let hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false, true);
hasDraftEntity.HasDraftEntity['@UI.Hidden'] = true;

@@ -782,3 +823,4 @@ addElement(hasDraftEntity, artifact);

uuidDraftKey = uuidDraftKey[0]; // filter returns an array, but it has only one element
createForeignKeyElement(draftAdministrativeData.DraftAdministrativeData, 'DraftAdministrativeData', uuidDraftKey, artifact, artifactName);
let path = ['definitions', artifactName, 'elements', 'DraftAdministrativeData', 'keys', 0];
createForeignKeyElement(draftAdministrativeData.DraftAdministrativeData, 'DraftAdministrativeData', uuidDraftKey, artifact, artifactName, path);
}

@@ -804,7 +846,2 @@ // SiblingEntity : Association to one <artifact> on (... IsActiveEntity unequal, all other key fields equal ...)

// Make all non-key elements nullable
if (elem.notNull && elem.key !== true) {
elem.notNull = false;
}
// Draft-enable the targets of composition elements (draft nodes), too

@@ -860,3 +897,3 @@ // TODO rewrite

// Common.ValueList in the final EDM.
// Do this only if the association is navigable and the enclosing artifact is
// Do this only if the association is navigable and the enclosing artifact is
// a service member (don't pollute the CSN with unnecessary annotations).

@@ -866,3 +903,3 @@ // TODO: test???

let vlAnno = '@Common.ValueList.viaAssociation';
if (isAssociation(member.type, csn)) {
if (isAssociation(member.type)) {
let navigable = member['@odata.navigable'] !== false; // navigable disabled only if explicitly set to false

@@ -910,2 +947,7 @@ let targetDef = getCsnDef(member.target);

function isLocalizedArtifactInService(artName, services) {
if (!artName.startsWith('localized.')) return false;
return isArtifactInSomeService(artName.split('.').slice(1).join('.'), services);
}
function getServiceOfArtifact(artName, services) {

@@ -912,0 +954,0 @@ return services.find(serviceName => artName.startsWith(`${serviceName}.`));

@@ -70,3 +70,3 @@ const { setProp, forEachDefinition, forEachGeneric, forEachMemberRecursively } = require('../base/model');

// then check its elements, if some of the elements:
// -> is an association - set a '_swaggerTarget' to the association with a value of the corresponding
// -> is an association - set a '_swaggerTarget' to the association with a value of the corresponding
// projection(on the target of the assoc in the underlying context) from the current service

@@ -73,0 +73,0 @@ // or throw an error if the target is not exposed in the current service

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

}
if(element.type._artifact.name.absolute == 'cds.Decimal' && element.precision === undefined && model.options.precision) {
element.precision = { literal: 'number', val: model.options.precision }
}
if(element.type._artifact.name.absolute == 'cds.Decimal' && element.scale === undefined && model.options.scale) {
element.scale = { literal: 'number', val: model.options.scale }
}
}

@@ -147,3 +141,3 @@ }

// Note that this must happen after struct flattening (because it assumes that the
// element names it encounters are relative to 'artifact').
// element names it encounters are relative to 'artifact').
// Return the newly generated foreign key element.

@@ -375,3 +369,3 @@ function createForeignKeyElement(assoc, foreignKey, artifact) {

// fused together into one step, using '_' (so that the path fits again for flattened
// structs), e.g.
// structs), e.g.
// [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in

@@ -416,3 +410,3 @@ // [ (Entity), (struct1_struct2_assoc), (elem) ]

result = result.slice(1);
}
}
return result;

@@ -693,3 +687,3 @@ }

// Add a default value 'defaultVal' if supplied
function createScalarElement(elemName, typeName, isKey, defaultVal) {
function createScalarElement(elemName, typeName, isKey, defaultVal, notNull=false) {
let type = model.definitions[typeName];

@@ -725,2 +719,5 @@ if (!type) {

}
if(notNull) {
elem.notNull = { val: true };
}
return elem;

@@ -970,5 +967,4 @@ }

* If the element has annotation @cds.valid.from or @cds.valid.to, return it.
*
*
* @param {any} element Element to check
* @param {boolean} [to=true] Wether to check for @cds.valid to or not
* @returns {Array[]} Array of arrays, first filed has an array with the element if it has @cds.valid.from, second field if it has @cds.valid.to. Default value is [] for each field.

@@ -995,3 +991,3 @@ */

* - The artifact is not a view
*
*
* Signals an error, if:

@@ -1001,3 +997,3 @@ * - The element is structured

* - Has an element as _parent.kind
*
*
* @param {any} annoName Annotation name

@@ -1020,3 +1016,3 @@ * @param {any} element Element to check

* Signals an error/warning if an annotation has been assigned more than once
*
*
* @param {any} array Array of elements that have the annotation

@@ -1023,0 +1019,0 @@ * @param {any} annoName Name of the annotation

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

// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
function getTransformers(model, pathDelimiter = '_') {
const { error, warning, signal } = alerts(model);
function getTransformers(model, options, pathDelimiter = '_') {
const { error, warning, signal } = alerts(model, options);
const {

@@ -87,3 +87,3 @@ getCsnDef,

// (1) replace all foreign keys that are managed associations themselves by
// their respective foreign keys, recursively, with names flattened using
// their respective foreign keys, recursively, with names flattened using
// pathDelimiter between path components.

@@ -169,14 +169,4 @@ // (2) replace all foreign keys that are structured with their respective flattened form.

// Assemble artificial foreign key element
let assocTargetDef = getCsnDef(assoc.target);
let fkArtifact = inspectRef(path).art;
let fkArtifact;
if (!path) {
fkArtifact = assocTargetDef.elements[foreignKey.ref.join(pathDelimiter)]; // foreignKey.as ???
} else {
const { art } = inspectRef(path);
fkArtifact = art;
}
// In case of compiler errors the foreign key might be missing

@@ -270,5 +260,6 @@ if (!fkArtifact && hasErrors((model.options && model.options.messages) || model.messages)) {

// Sanity checks
if (foreignKey.ref.length > 1) {
throw Error('Expecting foreign key ' + foreignKey.$generatedFieldName + ' to be flattened');
}
if(options && options.toOdata.odataFormat != 'structured')
if (foreignKey.ref.length > 1) {
throw Error('Expecting foreign key ' + foreignKey.$generatedFieldName + ' to be flattened');
}
if (!target) {

@@ -295,5 +286,5 @@ throw Error('Expecting target of association ' + assocName + ' to be resolved');

// @foo: true,
// elements:
// elements:
// { a: { type: 'cds.Integer' } },
// { b: {
// { b: {
// elements:

@@ -312,3 +303,3 @@ // { b1: type: 'cds.String', length: 42 } } },

// type: 'cds.String',
// length: 42 },
// length: 42 },
// }

@@ -392,8 +383,3 @@ function flattenStructuredElement(elem, elemName, ipath=[]) {

if (e.message && e.message == "Scope 'ref-where' but no entity was provided.") {
const main = path.slice(0, path.lastIndexOf("ref"));
const { links } = inspectRef(main);
const whereEntity = links[path[path.lastIndexOf("ref") + 1]].art;
return flatten(ref, path, whereEntity.target ? getCsnDef(whereEntity.target) : whereEntity);
return flatten(ref, path);
} else {

@@ -404,6 +390,6 @@ throw e;

function flatten(ref, path, whereEntity) {
function flatten(ref, path) {
let result = [];
//let stack = []; // IDs of path steps not yet processed or part of a struct traversal
const { links, scope } = inspectRef(path, whereEntity);
const { links, scope } = inspectRef(path);
if (scope === "$magic")

@@ -429,4 +415,4 @@ return ref;

// Flatten on conditions of unmanaged associations. This method assumes that
// other flattening of the elements was already performed and there are
// Flatten on conditions of unmanaged associations. This method assumes that
// other flattening of the elements was already performed and there are
// no left over structure elements marked with some ignore flag. Also, uses

@@ -455,3 +441,3 @@ // the non-enumerable property '_flatElementNameWithDots'.

let needToFlat = false;
// $user.locale must not be flatten and they are not understand by inspectRef

@@ -489,3 +475,3 @@ if (ref.join('.') === '$user.locale')

if (assocDef._flatElementNameWithDots)
signal(error`Redirection for sub elements not supported yet - association "${artName}.${assocName}"`);
signal(error`Redirection for sub elements not supported yet - association "${artName}.${assocName}"`, ['definitions', artName, 'elements', assocName]);
}

@@ -496,5 +482,5 @@ }

* Copy properties of the referenced type, but don't resolve to the final base type.
*
*
* @param {any} node Node to copy to
* @returns {undefined}
* @returns {void}
*/

@@ -679,6 +665,6 @@ function copyTypeProperties(node) {

// Add a default value 'defaultVal' if supplied
// example result: { foo: { type: 'cds.Integer', key: true, default: { val: 6 } } }
// ^^^ ^^^^^^^^^ ^^^^ ^^
// elemName typeName isKey defaultVal
function createScalarElement(elemName, typeName, isKey = false, defaultVal = undefined) {
// example result: { foo: { type: 'cds.Integer', key: true, default: { val: 6 }, notNull: true } }
// ^^^ ^^^^^^^^^ ^^^^ ^^ ^^
// elemName typeName isKey defaultVal notNull
function createScalarElement(elemName, typeName, isKey = false, defaultVal = undefined, notNull=false) {
if (!isBuiltinType(typeName) && !model.definitions[typeName]) {

@@ -693,3 +679,3 @@ throw new Error('Expecting valid type name: ' + typeName);

if (isKey) {
result[elemName].key = true
result[elemName].key = true;
}

@@ -701,2 +687,5 @@ if (defaultVal !== undefined) {

}
if(notNull) {
result[elemName].notNull = true;
}
return result;

@@ -763,3 +752,4 @@ }

// TODO: check the usage of this function's param 'keyElem' ?
function createForeignKey(keyElemName/*, keyElem */) {
function createForeignKey(keyElemName, keyElem = undefined) { /* eslint-disable-line no-unused-vars */
return {

@@ -793,6 +783,6 @@ ref: [keyElemName]

* Add element 'elem' to 'artifact'
*
*
* @param {any} elem is in form: { b: { type: 'cds.String' } }
* @param {any} artifact is: { kind: 'entity', elements: { a: { type: 'cds.Integer' } ... } }
* @returns {undefined}
* @param {any} artifact is: { kind: 'entity', elements: { a: { type: 'cds.Integer' } ... } }
* @returns {void}
*/

@@ -892,3 +882,3 @@ function addElement(elem, artifact) {

* If the element has annotation @cds.valid.from or @cds.valid.to, return it.
*
*
* @param {any} element Element to check

@@ -917,3 +907,3 @@ * @param {Array} path path in CSN for error messages

* - The artifact is not a view
*
*
* Signals an error, if:

@@ -923,5 +913,6 @@ * - The element is structured

* - Has an element as _parent.kind
*
*
* @param {any} annoName Annotation name
* @param {any} elementName Name of the element to be checked
* @param {any} element Element to be checked
* @param {any[]} path
* @param {any} artifact Artifact

@@ -943,3 +934,3 @@ * @returns {Boolean} True if no errors

* Signals an error/warning if an annotation has been assigned more than once
*
*
* @param {any} array Array of elements that have the annotation

@@ -963,3 +954,3 @@ * @param {any} annoName Name of the annotation

* Calls `callback` for each element in `elements` property of `artifact` recursively.
*
*
* @param artifact the artifact

@@ -966,0 +957,0 @@ * @param path path to get to `artifact` (mainly used for error messages)

'use strict'
const { setProp, forEachGeneric, forEachDefinition } = require('../base/model');
var { CompilationError, hasErrors, sortMessages } = require('../base/messages');
var { handleMessages } = require('../base/messages');
const modelUtils = require('../model/modelUtils.js');

@@ -10,4 +10,2 @@ const compactor = require('../json/compactor');

const {compactModel} = require('../json/to-csn');
const csn2xsn = require('../json/csn2xsn');
const { cloneCsn } = require('../model/csnUtils');
// Paths that start with an artifact of protected kind are special

@@ -18,3 +16,7 @@ // either ignore them in QAT building or in path rewriting

function translateAssocsToJoinsCSN(csn, options){
const model = csn2xsn(cloneCsn(csn), options);
let { augment } = require("../json/from-csn");
let xsn = augment(csn);
let compileSources = require('../main').compileSources;
const model = compileSources( { '<stdin>.csn': xsn }, options );
translateAssocsToJoins(model, options);
forEachDefinition(model, art => {

@@ -32,14 +34,12 @@ if (art.$queries) {

}
// else {
// // FIXME: is this really correct?
// query.columns = Object.values(query.elements);
// }
for (let elemName in query.elements) {
delete query.elements[elemName].viaAll;
}
}
}
});
translateAssocsToJoins(model);
// If A2J reports error - end! Continuing with a broken CSN makes no sense
if (hasErrors(model.messages)) {
throw new CompilationError(sortMessages(model.messages), csn)
}
handleMessages(model, options);
// FIXME: Move this somewhere more appropriate
const compact = compactModel(model);

@@ -51,7 +51,7 @@ setProp(compact, "options", csn.options);

function translateAssocsToJoins(model)
function translateAssocsToJoins(model, inputOptions = {})
{
const { error, signal } = alerts(model);
const { error, signal } = alerts(model, inputOptions);
var options = model.options || {};
var options = model.options || inputOptions;

@@ -62,3 +62,3 @@ const fullJoinOption = options.forHana && options.forHana.associations == 'joins';

const pathDelimiter = (options.forHana && options.forHana.names == 'hdbcds') ? '.' : '_';
const { compactNode } = compactor.getCompactors(options);

@@ -233,3 +233,3 @@

function createLeftOuterJoins(query, env)
{
{
if(query.op.val === 'query')

@@ -341,3 +341,3 @@ {

// be rendered verbatim
if((env.location === 'OrderBy' && !pathNode.path[0]._navigation))
if((env.location === 'OrderBy' && !pathNode.path[0]._navigation))
return;

@@ -593,3 +593,3 @@

env.firstAssocPathStep = fwdAssoc.name.id;
result.args.push(createOnCondition(fwdAssoc, tgtAlias, srcAlias));
result.args.push(createOnCondition(fwdAssoc, ...swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias)));
i += 2; // skip next two tokens and continue with loop

@@ -609,3 +609,3 @@ continue;

// If this is a backlink condition, produce the
// If this is a backlink condition, produce the
// ON cond of the forward assoc with swapped src/tgt aliases

@@ -615,27 +615,3 @@ let fwdAssoc = getForwardAssociationExpr(expr);

env.firstAssocPathStep = fwdAssoc.name.id;
let newSrcAlias = tgtAlias;
let newTgtAlias = {};
// first try to identify table alias for complex views or
// redirected associations
if(fwdAssoc._redirected) {
newTgtAlias.id = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.name.id;
newTgtAlias._artifact = fwdAssoc._redirected[fwdAssoc._redirected.length-1]._finalType;
newTgtAlias._navigation = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA._navigation;
}
else {
// if target was used as part of complex query (join in FROM clause) use
// ta.QA
let ta = env.lead.$tableAliases[fwdAssoc.target._artifact.name.id];
if(ta) {
let srcQA = ta.$QA;
newTgtAlias.id = srcQA.name.id;
newTgtAlias._artifact = srcQA._artifact;
newTgtAlias._navigation = srcQA._navigation;
}
else {
// last resort, use last original srcAlias
newTgtAlias = Object.assign(newTgtAlias, srcAlias);
}
}
let oc = createOnCondition(fwdAssoc, newSrcAlias, newTgtAlias);
let oc = createOnCondition(fwdAssoc, ...swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias));
return oc;

@@ -656,2 +632,35 @@ }

// The src/tgtAliases need to be swapped for ON Condition of the forward assoc.
// The correct table alias is the QA of the original target. If this target
// has been redirected, use the QA of the redirected target.
// As last resort use the source alias information.
function swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias) {
let newSrcAlias = tgtAlias;
let newTgtAlias = {};
// first try to identify table alias for complex views or
// redirected associations
if(fwdAssoc._redirected) {
newTgtAlias.id = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.name.id;
newTgtAlias._artifact = fwdAssoc._redirected[fwdAssoc._redirected.length-1]._finalType;
newTgtAlias._navigation = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA._navigation;
}
else {
// if target was used as part of complex query (join in FROM clause) use
// ta.QA
let ta = env.lead.$tableAliases[fwdAssoc.target._artifact.name.id];
if(ta) {
let srcQA = ta.$QA;
newTgtAlias.id = srcQA.name.id;
newTgtAlias._artifact = srcQA._artifact;
// sometimes _navigation can be found directly in the QA object, sometimes in the first path step
newTgtAlias._navigation = srcQA._navigation || (srcQA.path && srcQA.path[0]._navigation);
}
else {
// last resort, use last original srcAlias
newTgtAlias = Object.assign(newTgtAlias, srcAlias);
}
}
return [newSrcAlias, newTgtAlias];
}
function rewritePathNode(pathNode)

@@ -710,3 +719,3 @@ {

/* if the $projection path has association path steps make sure to address the
/* if the $projection path has association path steps make sure to address the
element by its last table alias name. Search from the end upwards

@@ -886,4 +895,4 @@ to the top for the first association path step and cut off path here.

}
return paths.map(p => {
return { id: (prefix ? prefix + pathDelimiter : '' ) + p.id, _artifact: p._artifact }
return paths.map(p => {
return { id: (prefix ? prefix + pathDelimiter : '' ) + p.id, _artifact: p._artifact }
} );

@@ -958,2 +967,7 @@ }

/**
* @param {any} assocStep
* @param {any[]} path
* @param {string} [pathStr='']
*/
function substituteFKAliasForPath(assocStep, path, pathStr='')

@@ -963,2 +977,3 @@ {

let ppt = assocStep._artifact.$fkPathPrefixTree.children;
/** @type any */
let fk = undefined; // last found FK

@@ -1011,3 +1026,3 @@ let fkPs = undefined; // last path step that found FK

return pathDict.$check;
pathDict.$check = true;

@@ -1075,3 +1090,3 @@ // all leaf types must be scalar in a query

}
/*

@@ -1366,5 +1381,5 @@ Create path prefix trees and merge paths into the trees depending on the path location.

* The final _artifact ref is set as _artifact ref to the path
*
* @param {Objects[]} pathSteps Array of [ 'pathStep id', _artifact reference, namedArgs (optional) ]
* @param {any} alias Alias to set as the name property -> { id: <alias> }
*
* @param {Object[]} pathSteps Array of [ 'pathStep id', _artifact reference, namedArgs (optional) ]
* @param {any} [alias] Alias to set as the name property -> { id: <alias> }
* @param {boolean} [rewritten=true] If true, mark the objects with $rewritten

@@ -1490,3 +1505,3 @@ * @returns {Object} CSN path

// Ask for Array before typeof object (which would also be true for Array)
if(Array.isArray(node))
if(Array.isArray(node))
node.map(n => walk(n, env));

@@ -1523,3 +1538,3 @@ // instanceof Object doesn't respect dictionaries...

If the filter becomes JOIN relevant, default FILTERS (part of the
If the filter becomes JOIN relevant, default FILTERS (part of the
association definition) MUST be CLONED to each assoc path step

@@ -1526,0 +1541,0 @@ BEFORE resolution.

@@ -96,4 +96,4 @@ /**

* in obj.
* @param {Array} obj
* @param {function} callback
* @param {Array} obj
* @param {(value: string, index: number, array: string[]) => boolean} callback
*/

@@ -176,2 +176,2 @@ function dsome(obj, callback) {

forEachProp
}
}
Object.defineProperty(module.exports, 'isTTY', {
get: () => process.stdin.isTTY && process.stdout.isTTY
Object.defineProperty(module.exports, 'isTTY', {
get: () => process.stdin.isTTY && process.stdout.isTTY
});

@@ -5,0 +5,0 @@

{
"name": "@sap/cds-compiler",
"version": "1.20.3",
"lockfileVersion": 1,
"requires": true,
"version": "1.21.1",
"dependencies": {
"antlr4": {
"version": "4.7.1",
"resolved": "http://nexus.wdf.sap.corp:8081/nexus/repository/build.releases.npm/antlr4/-/antlr4-4.7.1.tgz",
"integrity": "sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ=="
"version": "4.7.1"
},
"resolve": {
"version": "1.8.1",
"resolved": "http://nexus.wdf.sap.corp:8081/nexus/repository/build.releases.npm/resolve/-/resolve-1.8.1.tgz",
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
"requires": {
"path-parse": "1.0.5"
},
"dependencies": {
"path-parse": {
"version": "1.0.5",
"resolved": "http://nexus.wdf.sap.corp:8081/nexus/repository/build.releases.npm/path-parse/-/path-parse-1.0.5.tgz",
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
"version": "1.0.5"
}

@@ -28,7 +17,5 @@ }

"sax": {
"version": "1.2.4",
"resolved": "http://nexus.wdf.sap.corp:8081/nexus/repository/build.releases.npm/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
"version": "1.2.4"
}
}
}

@@ -1,1 +0,1 @@

{"bin":{"cdsc":"bin/cdsc.js","cdshi":"bin/cdshi.js","cdsse":"bin/cdsse.js"},"bundleDependencies":false,"dependencies":{"antlr4":"4.7.1","resolve":"1.8.1","sax":"1.2.4"},"deprecated":false,"description":"CDS (Core Data Services) compiler and backends","keywords":["CDS"],"main":"lib/main.js","name":"@sap/cds-compiler","version":"1.20.3","license":"SEE LICENSE IN developer-license-3.1.txt"}
{"bin":{"cdsc":"bin/cdsc.js","cdshi":"bin/cdshi.js","cdsse":"bin/cdsse.js"},"bundleDependencies":false,"dependencies":{"antlr4":"4.7.1","resolve":"1.8.1","sax":"1.2.4"},"deprecated":false,"description":"CDS (Core Data Services) compiler and backends","keywords":["CDS"],"main":"lib/main.js","name":"@sap/cds-compiler","version":"1.21.1","license":"SEE LICENSE IN developer-license-3.1.txt"}

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 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

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