Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sap/cds-compiler

Package Overview
Dependencies
Maintainers
1
Versions
108
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.45.0 to 1.46.4

lib/transform/odata/expandStructKeysInAssociations.js

35

CHANGELOG.md

@@ -9,2 +9,37 @@ # ChangeLog for cdx compiler and backends

## Version 1.46.4 - 2020-11-26
### Fixed
- Association to Join translation: Fix using forward association target as table alias in ON condition.
## Version 1.46.2 - 2020-11-20
### Fixed
- to.edm(x) Fix a bug in the alias calculation for key references in structured OData.
## Version 1.46.0 - 2020-11-20
### Changed
- to.edm(x):
+ V4 structured key ref path aliases are now the basenames, colliding aliases are numbered.
+ Lower level to `info` for "‹Term› is not applied" message if an annotation cannot be applied.
- OData:
+ Update vocabulary 'UI'
+ Correctly handle `not null` during flattening. Only if the parent and all subelements in the chain
are `not null`, make the corresponding flat leaf element `not null`.
### Fixed
- Do not consider events to be potential targets for implicit redirections:
strange warnings for multiple projections or other strange errors disappear.
- to.hdbcds/hdi/sql:
+ Reject structured view parameters for HANA.
+ Correctly handle `not null` during flattening.
Only if the parent and all subelements in the chain are `not null`, make the corresponding flat leaf element `not null`.
- to.edm(x): Render @assert.range enum annotations correctly (enum symbol as value and don't omit zero value).
- Fixed CDS module resolution with option `newResolve` on Windows where a superfluous `\` was prepended to absolute paths.
## Version 1.45.0 - 2020-10-30

@@ -11,0 +46,0 @@

2

doc/CHANGELOG_BETA.md

@@ -17,3 +17,3 @@ # ChangeLog of Beta Features for cdx compiler and backends

all generated texts entities additionally contain an element `language`
which is an association to `sap.common.Languages` using element `local`.
which is an association to `sap.common.Languages` using element `locale`.

@@ -20,0 +20,0 @@ ## Version 1.43.0

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

const alerts = require('../base/alerts');
const { getMessageFunction } = require('../base/messages');

@@ -12,2 +13,3 @@ // Check the annotation assignments (if any) of 'annotatable', possibly using annotation

const { warning, signal } = alerts(model);
const message = getMessageFunction(model);

@@ -52,5 +54,59 @@ // Iterate all annotations of 'annotatable'

// The annotation resolution will change in v2: Only absolute paths are allowed.
// To make the upgrade easier, warn for cases where a non-absolute path is used.
// Note that we cannot simply compare the first path segment or we would miss cases like:
//
// namespace test;
// annotation test { test: { str: String; } }
// @test.test.str: 'test'
// entity E { ... }
//
// We also cannot _always_ compare `artifactInV1` with `artifactInV2` as the path may
// be absolute and `path[0]` may refer to an USING, e.g.
//
// namespace ns;
// annotation test { str: String; }
// @(ns.test.str: 'some string') // path[0]._artifact !== xsn.definitions['ns']
// entity SomeEntity { key id : String; }
//
// So only if the first path segment resolves to an annotation, we check if the
// representation changes. Or if the first path segment refers to a (temporary)
// namespace, e.g. for:
//
// namespace test.test;
// annotation test.SecondLevelAnnotation { int: Integer; }
// @(test.SecondLevelAnnotation.int: 42)
// entity OtherEntity { key id : String; }
//
// The example above could be replaced by an absolute path
const oldName = anno.name.path[0].id;
const artifactInV1 = anno.name.path[0]._artifact;
const artifactInV2 = model.definitions[oldName];
if (artifactInV1.kind !== 'using' && artifactInV1 !== artifactInV2) {
const loc = anno.name.path[0].location || anno.name.location;
const canBeReplaced = !(annoDecl.name.absolute.startsWith(oldName));
// The used annotation path in the CDS source.
let oldFullName = anno.name.path.map(step => step && step.id).join('.');
// Remove the element path because we only check for definition resolution.
// Note that elementDecl may be an entity or other artifact at this stage.
if (elementDecl && elementDecl.name.element) {
const elementSuffix = elementDecl.name.element.length;
oldFullName = oldFullName.slice(0, oldFullName.length - elementSuffix - 1);
}
message('anno-name-resolution', loc, annotatable,
{
'#': canBeReplaced ? 'replace' : 'std',
anno: oldFullName,
alias: '@' + annoDecl.name.absolute
}, 'Warning',
{
std: 'The annotation $(ALIAS), not $(ANNO) is used with cds-compiler v1.x',
replace: 'The annotation $(ALIAS), not $(ANNO) is used with cds-compiler v1.x; replace the annotation accordingly if this is really intended'
}
);
}
// Must be an annotation if found
if (annoDecl.kind !== 'annotation') {
signal(warning`"${annoDecl.name.absolute}" is a ${annoDecl.kind}, not an annotation`, anno.location || anno.name.location);
// no warning anymore => behavior changes in v2
return;

@@ -57,0 +113,0 @@ }

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

// TODO: Check if the on-condition only references things inside of the .items
this.signal(this.error`Unmanaged associations in "array of" or "many" are not allowed.`, member.$path)
this.error(null, member.$path, `Unmanaged associations in "array of" or "many" are not allowed.`)
}

@@ -29,0 +29,0 @@ }

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

if(TableUdfCv.length > 1)
this.signal(this.error`Annotations ${TableUdfCv.join(', ')} cannot be used in combination`, path);
this.error(null, path, `Annotations ${TableUdfCv.join(', ')} cannot be used in combination`);
}

@@ -13,0 +13,0 @@ }

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

if(i>1)
this.signal(this.error`Illegal number of unary '+/-' operators`, path);
this.error(null, path, `Illegal number of unary '+/-' operators`);
}

@@ -27,3 +27,3 @@ }

if(member.default && prop === 'params' && this.csn.options.toHana) {
this.signal(this.error`Parameter default values are not supported in HANA CDS`, path);
this.error(null, path, `Parameter default values are not supported in HANA CDS`);
}

@@ -30,0 +30,0 @@ }

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

if(elementCount === 0){
this.signal(this.error`Empty structured types/elements must not be used as foreign keys.`, member.$path);
this.error(null, member.$path, `Empty structured types/elements must not be used as foreign keys.`);
}

@@ -50,5 +50,5 @@ }

if(mem.items){
this.signal(this.error`Array-like properties must not be foreign keys`, member.$path);
this.error(null, member.$path, `Array-like properties must not be foreign keys`);
} else if(isUnmanagedAssoc(mem)){
this.signal(this.error`Unmanaged association must not be a foreign key`, member.$path);
this.error(null, member.$path, `Unmanaged association must not be a foreign key`);
} else if(mem.keys){

@@ -55,0 +55,0 @@ handleAssociation(mem);

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

if(!mem.target && mem.targetAspect && typeof mem.targetAspect !== 'string')
this.signal(this.error`Types with anonymous aspect compositions cannot be used.`, member.$path);
this.error(null, member.$path, `Types with anonymous aspect compositions cannot be used.`);
} else if(mem.elements) {

@@ -56,0 +56,0 @@ handleStructured(mem, assertNoAnonymousAspectComposition);

@@ -114,6 +114,8 @@ 'use strict';

for(let j = 0; j < _links.length-1; j++){
const csnPath = path.concat(['on', i, 'ref', j]);
if(_links[j].art.target && !((_links[j].art === member) || ref[j] === '$self' || ref[j] === '$projection' || (validDollarSelf && j === _links.length - 1))){
if(_links[j].art.on){
// It's an unmanaged association - traversal is always forbidden
this.signal(this.error`ON-Conditions can not follow unmanaged associations, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}.`, path.concat(['on', i, 'ref', j]));
this.error(null, csnPath, `ON-Conditions can not follow unmanaged associations, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}.`);
} else {

@@ -123,3 +125,3 @@ // It's a managed association - access of the foreign keys is allowed

if(!_links[j].art.keys.some(ref => ref.ref[0] === nextRef)){
this.signal(this.error`ON-Conditions can only follow managed associations to the foreign keys of the managed association, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}`, path.concat(['on', i, 'ref', j]));
this.error(null, csnPath, `ON-Conditions can only follow managed associations to the foreign keys of the managed association, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}`);
}

@@ -130,7 +132,7 @@ }

if(ref[j].where){
this.signal(this.error`ON-Conditions must not contain filters, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}.`, path.concat(['on', i, 'ref', j]));
this.error(null, csnPath, `ON-Conditions must not contain filters, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}.`);
}
if(ref[j].args){
this.signal(this.error`ON-Conditions must not contain parameters, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}.`, path.concat(['on', i, 'ref', j]));
this.error(null, csnPath, `ON-Conditions must not contain parameters, step "${logReady(ref[j])}" of path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}.`);
}

@@ -149,6 +151,8 @@ }

/* 2) */ (_art.target && validDollarSelf))) {
this.signal(this.error`The last path of an on-condition must be a scalar value, path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}`, path.concat(['on', i,'ref',ref.length-1]))
this.error(null, path.concat(['on', i,'ref',ref.length-1]),
`The last path of an on-condition must be a scalar value, path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}`)
}
else if(_art.items){
this.signal(this.error`ON-Conditions can not use array-like elements, path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}`, path.concat(['on', i, 'ref', ref.length-1]));
this.error(null, path.concat(['on', i, 'ref', ref.length-1]),
`ON-Conditions can not use array-like elements, path ${ref.map(ps => `"${logReady(ps)}"`).join('.')}`);
}

@@ -155,0 +159,0 @@ }

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

*/
function csn2annotationEdm(csn, serviceName, options=undefined) {
function csn2annotationEdm(csn, edm, serviceName, options=undefined) {

@@ -226,18 +226,34 @@ if(!options)

// generate the edmx "frame" around the annotations
let schema = new Edm.Schema(v, serviceName, serviceName, undefined, g_annosArray, false);
let service = new Edm.DataServices(v, schema);
/** @type {object} */
let edm = new Edm.Edm(v, service);
// edm is undefined in testODataAnnotations.js
if(edm !== undefined) {
// distribute edm:Annotations into the schemas
// Create list of full qualified schema names (ServiceName[.subschema])
// Sort in reverse order for longest match
const fqSchemaNames = Object.keys(edm._service._schemas).sort((a,b) => b.length-a.length);
// Distribute each anno into Schema
g_annosArray.forEach(annos => {
const carrierName = annos.Target;
let targetSchema = fqSchemaNames.reduce((rc, sn) => !rc && carrierName && carrierName.startsWith(sn + '.') ? rc = sn : rc, undefined);
// if no target schema has been found, it's a service annotation that applies to the service schema
if(targetSchema === undefined)
targetSchema = serviceName;
if(targetSchema) {
if(targetSchema !== serviceName) {
annos.Target = annos.Target.replace(serviceName + '.', '');
}
edm._service._schemas[targetSchema]._annotations.push(annos);
}
});
// add references for the used vocabularies
knownVocabularies.forEach(n => {
if(vocabularyDefinitions[n].used) {
let r = new Edm.Reference(v, vocabularyDefinitions[n].ref);
r.append(new Edm.Include(v, vocabularyDefinitions[n].inc))
edm._defaultRefs.push(r);
}
})
// add references for the used vocabularies
knownVocabularies.forEach(n => {
if(vocabularyDefinitions[n].used) {
let r = new Edm.Reference(v, vocabularyDefinitions[n].ref);
r.append(new Edm.Include(v, vocabularyDefinitions[n].inc))
edm._defaultRefs.push(r);
}
})
}
return edm;
return g_annosArray;

@@ -486,3 +502,3 @@ //-------------------------------------------------------------------------------------------------

// find last . in name and insert "EntityContainer/"
alternativeEdmTargetName = (carrier.entitySetName || edmTargetName).replace(/\.(?=[^.]*$)/, '.EntityContainer/');
alternativeEdmTargetName = (carrier.$entitySetName || edmTargetName).replace(/\.(?=[^.]*$)/, '.EntityContainer/');
hasAlternativeCarrier = carrier.$hasEntitySet;

@@ -635,4 +651,5 @@ }

if(!addAnnotationFunc(anno, dictTerm && dictTerm.AppliesTo)) {
if(dictTerm && dictTerm.AppliesTo)
message(warning, context, 'Term "' + fullTermName + '" is not applied (AppliesTo="' + dictTerm.AppliesTo.join(' ') + '")');
if(dictTerm && dictTerm.AppliesTo) {
message(info, context, 'Term "' + fullTermName + '" is not applied (AppliesTo="' + dictTerm.AppliesTo.join(' ') + '")');
}
}

@@ -639,0 +656,0 @@ }

@@ -123,13 +123,32 @@ 'use strict';

options.serviceName = serviceCsn.name;
options.fqSchemaXRef = [serviceCsn.name];
const fqSchemaXRef = [serviceCsn.name];
options.whatsMySchemaName = function(n) {
return fqSchemaXRef.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') ? rc = sn : rc, undefined);
}
const schemas = { [serviceCsn.name]: { name: serviceCsn.name, fqName: serviceCsn.name, _csn: serviceCsn, container: true, definitions: Object.create(null) } };
Object.keys(csn.definitions).reduce((schemas, fqName) => {
const art = csn.definitions[fqName];
const schemas = {
[serviceCsn.name]: {
name: serviceCsn.name,
fqName: serviceCsn.name,
_csn: serviceCsn,
container: true,
definitions: Object.create(null)
}
};
const fqNameToSchema = { [serviceCsn.name]: schemas[serviceCsn.name] };
Object.entries(csn.definitions).reduce((schemas, [fqName, art]) => {
// add sub schemas
if(fqName.startsWith(serviceCsn.name + '.') && art.kind === 'context') {
options.fqSchemaXRef.push(fqName);
fqSchemaXRef.push(fqName);
// strip the toplevel service schema name
const name = fqName.replace(serviceCsn.name + '.', '');
schemas[name] = { name, fqName, _csn: art, container: false, definitions: Object.create(null) };
schemas[name] = {
name,
fqName,
_csn: art,
container: false,
definitions: Object.create(null)
};
fqNameToSchema[fqName] = schemas[name];
}

@@ -139,12 +158,11 @@ return schemas;

// sort schemas in reverse order to allow longest match
fqSchemaXRef.sort((a,b) => b.length-a.length);
// fill the schemas, unfortunately this can't be done in one step
// as all possible prefix combinations must be known in fqSchemaXRef
Object.keys(csn.definitions).reduce((schemas, name) => {
const art = csn.definitions[name];
Object.entries(csn.definitions).reduce((schemas, [name, art]) => {
// Identify service members by their definition name only, this allows
// to let the internal object.name have the sub-schema name.
let schemaName = options.fqSchemaXRef.reduce((a, n) => {
if(name.startsWith( n + '.')) a = n;
return a; }, undefined);
let schemaName = options.whatsMySchemaName(name);

@@ -186,7 +204,6 @@ if(schemaName && art.kind !== 'context') {

*/
const references = Object.keys(csn.definitions).reduce((references, fqName) => {
const art = csn.definitions[fqName];
const references = Object.entries(csn.definitions).reduce((references, [fqName, art]) => {
// add references
if(fqName.startsWith(serviceCsn.name + '.') && art.kind === 'reference') {
options.fqSchemaXRef.push(fqName);
fqSchemaXRef.push(fqName);
references.push(art);

@@ -197,12 +214,13 @@ }

fqSchemaXRef.sort((a,b) => b.length-a.length);
// bring the schemas in alphabetical order, service first, root last
const schemaNames = Object.keys(schemas).filter(n => n !== 'root' && n !== serviceCsn.name).sort();
schemaNames.splice(0,0, serviceCsn.name);
const sortedSchemaNames = Object.keys(schemas).filter(n => n !== 'root' && n !== serviceCsn.name).sort();
sortedSchemaNames.splice(0,0, serviceCsn.name);
if(schemas.root)
schemaNames.push('root');
sortedSchemaNames.push('root');
// finally create the schemas and register them in the service.
schemaNames.forEach(name => {
sortedSchemaNames.forEach(name => {
const schema = schemas[name];
service.append(createSchema(schema));
service.registerSchema(schema.fqName, createSchema(schema));
});

@@ -218,8 +236,16 @@

else {
const schema = { name: serviceCsn.name, _csn: serviceCsn, container: true, definitions: csn.definitions };
const schema = {
name: serviceCsn.name,
fqName: serviceCsn.name,
_csn: serviceCsn,
container: true,
definitions: csn.definitions
};
const LeadSchema = createSchema(schema);
service.append(LeadSchema);
service.registerSchema(schema.fqName, LeadSchema);
}
createAnnotations(edm);
// Create annotations and distribute into Schemas
translate.csn2annotationEdm(csn, edm, serviceCsn.name, options);
return edm

@@ -303,3 +329,3 @@

let EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
let EntitySetName = (entityCsn.entitySetName || entityCsn.name).replace(schemaNamePrefix, '');
let EntitySetName = (entityCsn.$entitySetName || entityCsn.name).replace(schemaNamePrefix, '');

@@ -638,4 +664,4 @@ let [ properties, hasStream ] = createProperties(entityCsn);

// differing set names (<T>Parameters => <T>, <T>Type => <T>Set)
let fromEntitySet = ( navigationProperty._csn._parent.entitySetName || fromEntityType).replace(schemaNamePrefix, '');
let toEntitySet = (navigationProperty._targetCsn.entitySetName || toEntityType).replace(schemaNamePrefix, '');
let fromEntitySet = ( navigationProperty._csn._parent.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
let toEntitySet = (navigationProperty._targetCsn.$entitySetName || toEntityType).replace(schemaNamePrefix, '');

@@ -759,15 +785,4 @@ // from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })

}
function createAnnotations(edm)
{
/** @type {object} */
let annoEdm = translate.csn2annotationEdm(csn, serviceCsn.name, options);
for(let i = 0; i < annoEdm.getSchemaCount(); i++)
{
edm.setAnnotations(annoEdm.getAnnotations(i), i);
}
edm._defaultRefs.push(...annoEdm._defaultRefs);
}
}
}
module.exports = { csn2edm, csn2edmAll };

@@ -300,6 +300,7 @@ // @ts-nocheck

{
constructor(v, schema)
constructor(v)
{
super(v);
this.append(schema);
this.set( { _schemas: Object.create(null) } );
if(this.v2)

@@ -311,2 +312,10 @@ this.setXml( { 'm:DataServiceVersion': '2.0' } )

registerSchema(fqName, schema)
{
if(!this._schemas[fqName]) {
this._schemas[fqName] = schema;
super.append(schema);
}
}
toJSONchildren(json)

@@ -602,8 +611,4 @@ {

if(options.fqSchemaXRef && this[typeName]) {
let schemaName = options.fqSchemaXRef.reduce((a, n) =>
{
if(this[typeName].startsWith(n+'.')) a=n;
return a;
}, undefined);
if(options.whatsMySchemaName && this[typeName]) {
let schemaName = options.whatsMySchemaName(this[typeName]);
if(schemaName && schemaName !== options.serviceName) {

@@ -653,15 +658,34 @@ this[typeName] = this[typeName].replace(options.serviceName + '.', '');

this.append(...properties);
const aliasXref = Object.create(null);
csn.$edmKeyPaths.forEach((p, i) => {
// If key is a path, prepare an alias for it
if(p[0].indexOf('/') > -1) {
// Limit Key length to 14+14+4 => 32 characters
let alias = (this.Name + p[0]).replace(/\.|\//g, '');
if(alias.length > 28) {
alias = alias.substr(0, 13)+ '__' +alias.substr(alias.length-13, alias.length);
csn.$edmKeyPaths.forEach(p => {
const [alias, ...tail] = p[0].split('/').reverse();
if(aliasXref[alias] === undefined)
aliasXref[alias] = 0;
else
aliasXref[alias]++;
// if it's a path, push the alias
if(tail.length > 0)
p.push(alias);
});
csn.$edmKeyPaths.slice().reverse().forEach(p => {
let alias = p[1];
if(alias)
{
const c = aliasXref[alias]--;
// Limit Key length to 32 characters
if(c > 0) {
if(alias.length > 28) {
alias = alias.substr(0, 13)+ '__' +alias.substr(alias.length-13, alias.length);
}
alias = alias+'_'+c.toString().padStart(3,0);
}
alias = alias+'_'+i.toString().padStart(3,0);
p.push(alias);
else if(alias.length > 32) {
alias = alias.substr(0, 15)+ '__' +alias.substr(alias.length-15, alias.length);
}
p[1] = alias;
}
});
if(csn.$edmKeyPaths && csn.$edmKeyPaths.length)

@@ -1107,5 +1131,5 @@ this.set( { _keys: new Key(v, csn.$edmKeyPaths) } );

{
/* short notation for Edm.Boolean, Edm.String and Edm.Float, see:
https://github.wdf.sap.corp/edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
*/
/* short notation for Edm.Boolean, Edm.String and Edm.Float, see internal project:
edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
*/
case 'Edm.Boolean':

@@ -1112,0 +1136,0 @@ v = (v=='true'?true:(v=='false'?false:v));

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

getNamespaceOfArtifact,
getContextOfArtifact,
addStringAnnotationTo,

@@ -211,2 +212,3 @@ getServiceName,

let lastDotIdx = name.lastIndexOf('.');
if (lastDotIdx === -1) return undefined;
while (model.definitions[name]) {

@@ -221,3 +223,20 @@ if (model.definitions[name].kind === 'namespace')

}
/**
* Return the context part of the artifact name if any.
* @param {string} name Absolute name of artifact
*/
function getContextOfArtifact(name) {
let lastDotIdx = name.lastIndexOf('.');
while (model.definitions[name]) {
if (model.definitions[name].kind === 'context')
return name;
lastDotIdx = name.lastIndexOf('.');
if (lastDotIdx === -1) return undefined;
name = name.substring(0, lastDotIdx);
}
return undefined;
}
/**
* Add an annotation with absolute name 'absoluteName' (including the at-sign) and string value 'theValue' to 'node'

@@ -224,0 +243,0 @@ *

@@ -257,4 +257,4 @@

}
let tableName = quoteSqlId(absoluteCdsName(artifactName));
definitionsDuplicateChecker.addArtifact(tableName, art && art.$location, artifactName)
let tableName = quoteSqlId(absoluteCdsName(artifactName), art.$location);
definitionsDuplicateChecker.addArtifact(tableName, art.$location, artifactName)
result += 'TABLE ' + tableName;

@@ -273,3 +273,3 @@ result += ' (\n';

.filter(name => !art.elements[name]._ignore)
.map(name => quoteSqlId(name))
.map(name => quoteSqlId(name, art.elements[name].$location))
.join(', ');

@@ -323,3 +323,3 @@ let uniqueFields = Object.keys(art.elements).filter(name => art.elements[name].unique && !art.elements[name]._ignore)

let tableName = quoteSqlId(absoluteCdsName(artifactName));
let tableName = quoteSqlId(absoluteCdsName(artifactName), art.$location);
extensionsDuplicateChecker.addArtifact(tableName, art && art.$location, artifactName)

@@ -390,4 +390,4 @@

}
const quotedElementName = quoteSqlId(elementName);
duplicateChecker.addElement(quotedElementName, elm && elm.$location, elementName);
const quotedElementName = quoteSqlId(elementName, elm.$location);
duplicateChecker.addElement(quotedElementName, elm.$location, elementName);

@@ -431,3 +431,3 @@ let result = env.indent + quotedElementName + ' '

result += ' JOIN ';
result += quoteSqlId(absoluteCdsName(elm.target)) + ' AS ' + quoteSqlId(elementName) + ` ON (`;
result += quoteSqlId(absoluteCdsName(elm.target)) + ' AS ' + quoteSqlId(elementName, elm.$location) + ` ON (`;
result += renderExpr(elm.on, env) + ')';

@@ -602,5 +602,8 @@ }

}
else if (getLastPartOf(result) != quoteSqlId(implicitAlias)) {
// Render an artificial alias if the result would produce a different one
result += ' AS ' + quoteSqlId(implicitAlias);
else {
const quotedAlias = quoteSqlId(implicitAlias);
if (getLastPartOf(result) != quotedAlias) {
// Render an artificial alias if the result would produce a different one
result += ' AS ' + quotedAlias;
}
}

@@ -706,3 +709,3 @@ return result;

env._artifact = art;
let viewName = quoteSqlId(absoluteCdsName(artifactName));
let viewName = quoteSqlId(absoluteCdsName(artifactName), art.$location);
definitionsDuplicateChecker.addArtifact(viewName, art && art.$location, artifactName)

@@ -848,5 +851,5 @@ let result = 'VIEW ' + viewName;

}
let typeName = quoteSqlId(absoluteCdsName(artifactName));
const typeName = quoteSqlId(absoluteCdsName(artifactName), art.$location);
definitionsDuplicateChecker.addArtifact(typeName, art && art.$location, artifactName)
let result = 'TYPE ' + quoteSqlId(absoluteCdsName(artifactName)) + ' AS TABLE (\n';
let result = 'TYPE ' + typeName + ' AS TABLE (\n';
let childEnv = increaseIndent(env);

@@ -1208,3 +1211,3 @@ if (art.elements) {

// Complain about names that collide with known SQL keywords or functions
function quoteSqlId(name) {
function quoteSqlId(name, location=null) {
if (options.toSql.dialect === 'sqlite' && keywords.sqlite.includes(name.toUpperCase())) {

@@ -1215,3 +1218,3 @@ // Sanity check

}
signal(warning`The identifier "${name}" is a SQLite keyword`);
signal(warning`The identifier "${name}" is a SQLite keyword`, location);
}

@@ -1221,3 +1224,3 @@ if (options.toSql.names === 'plain') {

if (keywords.hana.includes(name.toUpperCase())) {
signal(warning`The identifier "${name}" is a HANA keyword`);
signal(warning`The identifier "${name}" is a HANA keyword`, location);
}

@@ -1224,0 +1227,0 @@ }

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

const { handleMessages } = require('../base/messages');
const { setProp } = require('../base/model');
const { setProp, isBetaEnabled } = require('../base/model');
const transformUtils = require('./transformUtilsNew');
const { mergeOptions, copyAnnotations } = require('../model/modelUtils');
const { mergeOptions } = require('../model/modelUtils');
const { getUtils,

@@ -26,7 +26,8 @@ cloneCsn,

const typesExposure = require('./odata/typesExposure');
const { isArtifactInSomeService, getServiceOfArtifact, isLocalizedArtifactInService } = require('./odata/utils');
const typesExposure = require('./odata/typesExposure');
const ReferenceFlattener = require('./odata/referenceFlattener');
const structureFlattener = require('./odata/structureFlattener');
const processForeignKeys = require('./odata/foreignKeys');
const { flattenCSN } = require('./odata/structureFlattener');
const processForeignKeys = require('./odata/generateForeignKeyElements');
const expandStructKeysInAssociations = require('./odata/expandStructKeysInAssociations');

@@ -93,4 +94,3 @@ // Transformation for ODATA. Expects a CSN 'inputModel', processes it for ODATA.

const { error, warning, info, signal } = alerts(csn, options);
const { error: _error, warning: _warning, info: _info } = alerts.makeMessageFunction(csn, options);
const { error, warning, info } = alerts.makeMessageFunction(csn, options);

@@ -163,3 +163,3 @@ // the new transformer works only with new CSN

if (validKey.length && !(validFrom.length && validTo.length)) {
_error(null, path, '@cds.valid.key was used but @cds.valid.from and @cds.valid.to are missing');
error(null, path, '@cds.valid.key was used but @cds.valid.from and @cds.valid.to are missing');
}

@@ -184,3 +184,3 @@ });

validator(csn, {
error, warning, info, signal, inspectRef, effectiveType, artifactRef, csn,
error, warning, info, inspectRef, effectiveType, artifactRef, csn,
},

@@ -209,3 +209,3 @@ /* Member Validators */ [ validateOnCondition, validateForeignKeys, validateAssociationsInArrayOf, validateDefaultValues ],

if (!finalTypeDef.type) {
_error(null, ['definitions', defName], { name: defName }, `${ defName } has no final type`);
error(null, ['definitions', defName], { name: defName }, `${ defName } has no final type`);
return;

@@ -218,3 +218,3 @@ }

} catch (ex) {
_error(null, ['definitions', defName], { name: defName }, `Final base type of ${ defName } not found`);
error(null, ['definitions', defName], { name: defName }, `Final base type of ${ defName } not found`);
return

@@ -275,3 +275,3 @@ }

if (!structuredOData) {
structureFlattener(csn, { csnUtils, cloneCsn, error, signal, forEachDefinition, setProp, referenceFlattener, copyAnnotations })
flattenCSN(csn, csnUtils, referenceFlattener, error);
}

@@ -283,4 +283,5 @@

// Expose user-defined types and anonymous types
typesExposure(csn, services, options, csnUtils, signal, referenceFlattener);
if (!(structuredOData && (isBetaEnabled(options, 'odataProxies') && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs))))
// Expose user-defined types and anonymous types
typesExposure(csn, services, options, csnUtils, { error }, referenceFlattener);

@@ -291,5 +292,8 @@ // flatten references, attach new paths

// Process associations - expand, generate foreign keys
let flatKeys = !structuredOData || (structuredOData && options.toOdata.odataForeignKeys);
processForeignKeys(csn, flatKeys, { referenceFlattener, csnUtils, transformers })
// Process associations
// 1. expand structured foreign keys, rewrite the 'ref' for such keys
expandStructKeysInAssociations(csn, referenceFlattener, csnUtils);
// 2. generate foreign keys for managed associations
processForeignKeys(csn, flatKeys, referenceFlattener, csnUtils, transformers, error);

@@ -318,3 +322,3 @@ // Flatten on-conditions in unmanaged associations

if (!isArtifactInSomeService(defName, services)) {
_warning(null, ['definitions', defName], { art: defName },
warning(null, ['definitions', defName], { art: defName },
`Ignoring annotation "@odata.draft.enabled" because artifact "${ defName }" is not part of a service`);

@@ -345,3 +349,3 @@ }

if (def.kind === 'type' && options.toOdata.version === 'v2') {
_warning(null, path,
warning(null, path,
`"${defName}.${memberName}": Structured types must not contain associations for OData V2`);

@@ -353,3 +357,3 @@ }

if (options.toOdata.version === 'v2') {
_error(null, path,
error(null, path,
`"${defName}.${memberName}": Element must not be an "array of" for OData V2`);

@@ -361,3 +365,3 @@ }

// if (member.items.elements && !member.items.type) {
// signal(error`"${defName}.${memberName}": Element must not be an "array of anonymous type"`, path);
// error(null, path, `"${defName}.${memberName}": Element must not be an "array of anonymous type"`);
// }

@@ -373,7 +377,7 @@ }

// (4.5) https://github.wdf.sap.corp/cdx/cds-compiler/issues/837
// (4.5) cdx/cds-compiler#837
// add check here for @Analytics.Measure and @Aggregation.default
// @Analytics has scope element
if (member['@Analytics.Measure'] && !member['@Aggregation.default']) {
_info(null, path, // ['definitions', defName, 'elements', memberName]
info(null, path, // ['definitions', defName, 'elements', memberName]
`'@Analytics.Measure' expects '@Aggregation.default' to be assigned as well in element '${defName}.${memberName}'`,

@@ -424,3 +428,3 @@ );

if (illV2Prefix.test(elemName)) {
_error(null, ['definitions', defName, 'elements', elemName],
error(null, ['definitions', defName, 'elements', elemName],
`"${defName}.${elemName}: Element name must not begin with '${elemName[0]}' for OData V2`);

@@ -440,3 +444,3 @@ }

if (mediaTypes.length > 1) {
_error(null, ['definitions', defName], `"${defName}: Multiple elements [${mediaTypes.map(e => e[0]).join(', ')}] annotated with '@Core.MediaType', OData V2 allows only one`);
error(null, ['definitions', defName], `"${defName}: Multiple elements [${mediaTypes.map(e => e[0]).join(', ')}] annotated with '@Core.MediaType', OData V2 allows only one`);
}

@@ -449,3 +453,3 @@ }

if (!allowedTypes.includes(e[1].type)) {
_error(null, ['definitions', defName, 'elements', e[0]], `"${defName}.${e[0]}": Element annotated with '@Core.MediaType' must be of either type "${allowedTypes.join(', ')}"`);
error(null, ['definitions', defName, 'elements', e[0]], `"${defName}.${e[0]}": Element annotated with '@Core.MediaType' must be of either type "${allowedTypes.join(', ')}"`);
}

@@ -531,3 +535,3 @@ });

rewriteCapabilities = false;
_warning(null, path, '"@readonly" and "@insertonly" cannot be assigned in combination');
warning(null, path, '"@readonly" and "@insertonly" cannot be assigned in combination');
}

@@ -581,3 +585,7 @@ for (let name in node) {

let result = { '@Core.SymbolicName': enumName };
if (node.enum[enumName].val) result.Value = node.enum[enumName].val;
if (node.enum[enumName].val !== undefined)
result.Value = node.enum[enumName].val;
else if(node.type && node.type === 'cds.String')
// the symbol is used as value only for type 'cds.String'
result.Value = enumName;
return result;

@@ -599,3 +607,3 @@ });

if (typeof node[name] !== 'boolean' && typeof node[name] !== 'string') {
_warning(null, defPath, { name }, `Annotation "${ name }" must have a string or boolean value`);
warning(null, defPath, { name }, `Annotation "${ name }" must have a string or boolean value`);
}

@@ -674,3 +682,3 @@ }

if (keys.length !== 1) {
_warning(null, ['definitions', artifactName], `"${artifactName}": "@odata.draft.enabled" - Entity should expose exactly one key element`);
warning(null, ['definitions', artifactName], `"${artifactName}": "@odata.draft.enabled" - Entity should expose exactly one key element`);
}

@@ -682,3 +690,3 @@

if (uuidCount === 0) {
_warning(null, ['definitions', artifactName], `"${artifactName}": "@odata.draft.enabled" - Entity key element should be of type "cds.UUID"`);
warning(null, ['definitions', artifactName], `"${artifactName}": "@odata.draft.enabled" - Entity key element should be of type "cds.UUID"`);
}

@@ -694,3 +702,3 @@

if (draftAdminDataProjection.kind !== 'entity' || !draftAdminDataProjection.elements['DraftUUID']) {
_error(null, ['definitions', draftAdminDataProjectionName], { name: draftAdminDataProjectionName },
error(null, ['definitions', draftAdminDataProjectionName], { name: draftAdminDataProjectionName },
`Generated entity "${ draftAdminDataProjectionName }" conflicts with existing artifact`);

@@ -776,7 +784,7 @@ }

if (hasBoolAnnotation(draftNode, '@odata.draft.enabled', true)) {
_error(null, ['definitions', artifactName, 'elements', elemName], `"${artifactName}.${elemName}": Composition in draft-enabled entity cannot lead to another entity with "@odata.draft.enabled"`);
error(null, ['definitions', artifactName, 'elements', elemName], `"${artifactName}.${elemName}": Composition in draft-enabled entity cannot lead to another entity with "@odata.draft.enabled"`);
}
// Ignore composition if not part of a service
else if (!getServiceName(elem.target)) {
_warning(null, ['definitions', artifactName, 'elements', elemName], `Target "${elem.target}" of composition "${artifactName}.${elemName}" cannot be a draft node because it is not part of a service`);
warning(null, ['definitions', artifactName, 'elements', elemName], `Target "${elem.target}" of composition "${artifactName}.${elemName}" cannot be a draft node because it is not part of a service`);
continue;

@@ -783,0 +791,0 @@ }

@@ -0,3 +1,45 @@

'use strict';
const { copyAnnotations } = require('../../model/modelUtils');
const { setProp } = require('../../base/model');
const { cloneCsn, forEachDefinition } = require('../../model/csnUtils');
// these functions are used for propagation of the annotations, collected along the path during flattening
const { addAnnotationsForPropagationFromElement, propagateAnnotationsToElement, resetAnnotationsForRropagation } = function () {
let toBePropagatedAnnotations = Object.create(null);
return {
addAnnotationsForPropagationFromElement: function (element) {
copyAnnotations(element, toBePropagatedAnnotations);
},
propagateAnnotationsToElement: function (element) {
copyAnnotations(toBePropagatedAnnotations, element);
},
resetAnnotationsForRropagation: function () {
toBePropagatedAnnotations = Object.create(null);
}
}
}();
// keep here the state of the 'notNull' attribute
// this is needed because during flattening all the elements
// along the chain need to be assigned with not null so
// the resulting element to be not null as well
const { isNotNull, setNotNull, setUpNotNull } = function () {
let notNull = undefined;
return {
isNotNull: function () {
return notNull;
},
setNotNull: function (value) {
notNull = value;
},
setUpNotNull: function (element, isParentNotNull) {
if (isParentNotNull && element.notNull) setNotNull(element.notNull);
else if (isNotNull() && !element.notNull || (isNotNull() === false && element.notNull !== false)) setNotNull(undefined);
}
}
}();
/**
* During the OData transfromations in flat-mode, all structured elements will be flattened.
* During the OData transformations in flat-mode, all structured elements will be flattened.
* This module performs the complete flattening.

@@ -9,69 +51,106 @@ * It also provides information to the reference flattener: elements produced for specific path in the CSN structure.

* @param {*} csn CSN-object to flatten
* @param {*} functions instances of utility functions
* @param {*} csnUtils instances of utility functions
*/
function flattenCSN(csn, functions) {
const { forEachDefinition } = functions;
forEachDefinition(csn, (def, _defName, _propertyName, path) => {
flattenDefinition(def, path, functions);
})
function flattenCSN(csn, csnUtils, referenceFlattener, signal) {
forEachDefinition(csn, (def, defName, propertyName, path) =>
flattenDefinition(def, path, csnUtils, referenceFlattener, signal));
}
/**
* Flattens one single definition and all structures in it
* Flattens one single definition and all structures in it. Modifies the definition in place.
* @param {*} definition definition object to flatten
* @param {*} definitionPath path in CSN object
* @param {*} functions utility functions
* @param {*} csnUtils utility functions
*/
function flattenDefinition(definition, definitionPath, functions) {
const { csnUtils, cloneCsn, error, signal, setProp, copyAnnotations } = functions;
if (definition.kind !== 'entity' && definition.kind !== 'view')
return;
function flattenDefinition(definition, definitionPath, csnUtils, referenceFlattener, signal) {
if (definition.kind !== 'entity' && definition.kind !== 'view') return;
let referenceFlattener = functions.referenceFlattener;
let { newFlatElements } = flattenStructure(definition, definitionPath, csnUtils, signal, referenceFlattener);
// these functions are used for propagation of the annotations, collected along the path during flattening
const { addAnnotationsForPropagationFromElement, propagateAnnotationsToElement, resetAnnotationsForRropagation } = function () {
let toBePropagatedAnnotations = Object.create(null);
return {
addAnnotationsForPropagationFromElement: function (element) {
copyAnnotations(element, toBePropagatedAnnotations);
},
propagateAnnotationsToElement: function (element) {
copyAnnotations(toBePropagatedAnnotations, element);
},
resetAnnotationsForRropagation: function () {
toBePropagatedAnnotations = Object.create(null);
}
referenceFlattener.attachPaths(newFlatElements, definitionPath.concat('elements'));
definition.elements = newFlatElements;
} // flattenDefinition
/**
* Flattenes structured element by calling element flattener for each structured child.
* Returns a dictionary containing all the new elements for the given structure.
* @param {*} struct the structure to flatten
* @param {*} path the path of the structure in the CSN tree
* @param {*} isTopLevelElement states if this is a top level element
* @param {*} elementPathInStructure list of parent element names
* @param {*} isKey true if this or the parent element is a key - will be propagated to all child elements
*/
function flattenStructure(struct, path, csnUtils, error, referenceFlattener = undefined, elementPathInStructure = [],
newFlatElements = Object.create(null), isTopLevelElement = true, isKey = false, propagateAnnotations = false, isParentNotNull = false) {
isTopLevelElement ? resetAnnotationsForRropagation() : addAnnotationsForPropagationFromElement(struct);
let generatedNewFlatElementsNames = []; // holds the names of all new child elements of the structure
for (let elementName in struct.elements) {
let element = struct.elements[elementName];
let currPath = path.concat('elements', elementName);
if (isTopLevelElement) {
isKey = element.key;
setNotNull(element.notNull)
} else {
setUpNotNull(element, isParentNotNull);
}
}();
let newElements = {};
flattenStructure(definition, definitionPath);
referenceFlattener.attachPaths(newElements,definitionPath.concat('elements'))
definition.elements = newElements;
// flat elements when structured and NOT empty (allow incomplete structures - cds-compiler#4337)
if (csnUtils.isStructured(element) && !(element.elements && Object.keys(element.elements).length === 0)) {
if (referenceFlattener) referenceFlattener.registerFlattenedElement(currPath, element.$path);
addAnnotationsForPropagationFromElement(element);
// if the child element is structured itself -> needs to be flattened
const subStruct = element.elements ? element : csnUtils.getFinalBaseType(element.type);
let result = flattenStructure(subStruct, currPath, csnUtils, error, referenceFlattener, elementPathInStructure.concat(elementName), newFlatElements, false, isKey || element.key, true, isNotNull());
generatedNewFlatElementsNames.push(...result.generatedNewFlatElementsNames); // accomulate names of produced elements
} else { // when we do not need to flat, this is scalar or empty (cds-compiler#4337) -> needs to be registered in referenceFlattener
let newElementName = elementPathInStructure.concat(elementName).join('_');
let elementNameWithDots = elementPathInStructure.concat(elementName).join('.');
addNewElementToResult(element, newElementName, elementNameWithDots, currPath);
}
}
if (referenceFlattener) {
referenceFlattener.registerGeneratedElementsForPath(path, generatedNewFlatElementsNames);
}
return { newFlatElements, generatedNewFlatElementsNames };
// adds newly created element into the final dictionary of elements
function addNewElement(element, elementName, elementNameWithDots, path, key, topLevel, propagateAnnotations) {
if (newElements[elementName]) {
signal(error`Generated element ${elementName} conflicts with other generated element`, path);
return undefined;
function addNewElementToResult(element, elementName, elementNameWithDots, path) {
if (newFlatElements[elementName]) {
error(null, path, `Generated element ${elementName} conflicts with other generated element`);
} else {
let newElement = createNewElement(element, key, elementNameWithDots, topLevel, propagateAnnotations);
let newPath = definitionPath.concat('elements',elementName)
newElements[elementName] = newElement;
referenceFlattener.registerElementTransition(path,newPath);
return newElement;
let newElement = createNewElement(element, elementNameWithDots);
newFlatElements[elementName] = newElement;
generatedNewFlatElementsNames.push(elementName);
if (referenceFlattener) {
let newPath = path.slice(0, 2).concat('elements', elementName);
referenceFlattener.registerElementTransition(path, newPath);
let movedTo = referenceFlattener.getElementTransition(path)
if (movedTo) {
setProp(newElement, '$paths', [movedTo]); // moved always on top-level -> new $paths has only one path element
}
}
}
} // addNewElement
} // addNewElementToResult
// creates new element by copying the properties of the originating element
function createNewElement(element, isKey, elementNameWithDots, topLevel, propagateAnnotations) {
function createNewElement(element, elementNameWithDots) {
let newElement = cloneCsn(element);
if (propagateAnnotations) propagateAnnotationsToElement(newElement);
if (isKey)
newElement.key = true;
if (!topLevel) {
if (isNotNull() === undefined) delete newElement.notNull;
if (isKey) newElement.key = true;
if (!isTopLevelElement) {
setProp(newElement, '$viaTransform', true);

@@ -83,61 +162,4 @@ setProp(newElement, '_flatElementNameWithDots', elementNameWithDots);

/**
* Flattenes structured element by calling element flattener for each structured child.
* @param {*} struct the structure to flatten
* @param {*} path the path of the structure in the CSN tree
* @param {*} isDefinition states if this is a top level element
* @param {*} elementPathInStructure list of parent element names
* @param {*} isKey true if this or the parent element is a key - will be propagated to all child elements
*/
function flattenStructure(struct, path, isDefinition = true, elementPathInStructure = [], isKey = false, propagateAnnotations = false) {
isDefinition ? resetAnnotationsForRropagation() : addAnnotationsForPropagationFromElement(struct);
} // flattenStructure
let resultingElementNames = []; // holds the names of all child elements of the structure
for (let elementName in struct.elements) {
let ikey = isKey; // parent element is key
let element = struct.elements[elementName];
if (element.key) ikey = true; // current element is key
let ipath = path.concat('elements', elementName);
// flat elements when structured and NOT empty (allow incomplete structures - cds-compiler#4337)
if (csnUtils.isStructured(element) && !(element.elements && Object.keys(element.elements).length === 0)) {
let namesOfCreatedElements = flattenStructuredElement(element, elementPathInStructure.concat(elementName), ipath, ikey);
resultingElementNames = resultingElementNames.concat(namesOfCreatedElements); // accomulate names of produced elements
} else { // when we do not need to flat, this is scalar or empty (cds-compiler#4337) -> needs to be registered in referenceFlattener
let newElementName = elementPathInStructure.concat(elementName).join('_');
let elementNameWithDots = elementPathInStructure.concat(elementName).join('.');
let newElement = addNewElement(element, newElementName, elementNameWithDots, ipath, ikey, isDefinition, propagateAnnotations);
if(newElement) {
resultingElementNames.push(newElementName);
let movedTo = referenceFlattener.getElementTransition(ipath)
if(movedTo) {
setProp(newElement,'$paths',[movedTo]); // moved always on top-level -> new $paths has only one path element
}
}
}
}
if (referenceFlattener) {
referenceFlattener.registerGeneratedElementsForPath(path, resultingElementNames);
}
return resultingElementNames;
} // flattenStructure
// flattenes on single structured element by calling the structure flattener for it
function flattenStructuredElement(element, elementPathInStructure, path, key) {
if (referenceFlattener)
referenceFlattener.registerFlattenedElement(path, element.$path);
let elemType;
if (!element.elements) { // structures do not have final base type
elemType = csnUtils.getFinalBaseType(element.type);
addAnnotationsForPropagationFromElement(element);
}
const struct = elemType ? elemType : element;
//TODO what happens with 'path' if element has base type?
return flattenStructure(struct, path, false, elementPathInStructure, key || element.key, true);
} // flattenStructuredElement
} // flattenDefinition
module.exports = flattenCSN;
module.exports = { flattenCSN, flattenStructure };

@@ -13,6 +13,17 @@ 'use strict';

module.exports = function (csn, services, options, csnUtils, signal, referenceFlattener) {
/**
* @param {CSN.Model} csn
* @param {string[]} services
* @param {CSN.Options} options
* @param {*} csnUtils
* @param {object} message message object with { error } function
* @param {*} referenceFlattener
*/
function typesExposure(csn, services, options, csnUtils, message, referenceFlattener = undefined) {
const { error } = message;
// are we working with structured OData or not
const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
// are we working with OData proxies or cross-service refs
const isMultiSchema = structuredOData && (isBetaEnabled(options, 'odataProxies') && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs));
// collect in this variable all the newly exposed types

@@ -30,3 +41,3 @@ let exposedStructTypes = [];

if (propertyName === 'elements') {
exposeStructTypeOf(element, `${defName}.${elementName}`, getServiceOfArtifact(defName, services), `${defName.replace(/\./g, '_')}_${elementName}`, structuredOData, path);
exposeStructTypeOf(element, `${defName}.${elementName}`, getServiceOfArtifact(defName, services), `${isMultiSchema ? defName : defName.replace(/\./g, '_')}_${elementName}`, structuredOData, path);
// TODO: use the next line once the array of logic is reworked

@@ -53,3 +64,3 @@ // exposeTypeOf(element, elementName, getServiceOfArtifact(defName, services), `${defName.replace(/\./g, '_')}_${elementName}`);

if (csnUtils.isStructured(element)) {
exposeStructTypeOf(element, elementName, serviceName, `${defNameWithoutServiceName(defName, serviceName).replace(/\./g, '_')}_${elementName}`, structuredOData, path);
exposeStructTypeOf(element, elementName, serviceName, `${isMultiSchema ? defNameWithoutServiceName(defName, serviceName) : defNameWithoutServiceName(defName, serviceName).replace(/\./g, '_')}_${elementName}`, structuredOData, path);
// TODO: use the next line once the array of logic is reworked

@@ -60,3 +71,3 @@ // exposeTypeOf(element, elementName, getServiceOfArtifact(defName, services), `${defName.replace(/\./g, '_')}_${elementName}`);

if (csnUtils.getServiceName(defName) && !element.type && !element.items && !element.elements) {
signal(signal.error`Element "${defName}.${elementName}" does not have a type: Elements of ODATA entities must have a type`, path);
error(null, path, `Element "${defName}.${elementName}" does not have a type: Elements of ODATA entities must have a type`);
}

@@ -88,3 +99,4 @@ }

exposedStructTypes.forEach(typeName => referenceFlattener.attachPaths(csn.definitions[typeName], ['definitions', typeName]))
if (referenceFlattener)
exposedStructTypes.forEach(typeName => referenceFlattener.attachPaths(csn.definitions[typeName], ['definitions', typeName]))

@@ -109,4 +121,4 @@ // still WIP function

* @param {Object} action
* @param {String} actionName
* @param {String} service
* @param {String} actionName
* @param {String} service
*/

@@ -126,6 +138,6 @@ function exposeTypesOfAction(action, actionName, service, path) {

* for a value of the 'node.type' property.
* @param {Object} node
* @param {String} memberName
* @param {String} service
* @param {String} artificialName
* @param {Object} node
* @param {String} memberName
* @param {String} service
* @param {String} artificialName
*/

@@ -144,6 +156,6 @@ function exposeStructTypeOf(node, memberName, service, artificialName, deleteElems = structuredOData, path) {

let typeDef = node.type ? csnUtils.getCsnDef(node.type) : /* structure|anonymous type */ node;
let newTypeId = node.type ? `${node.type.replace(/\./g, '_')}` : artificialName;
let newTypeId = node.type ? `${isMultiSchema ? node.type : node.type.replace(/\./g, '_')}` : artificialName;
let newTypeFullName =
(structuredOData && (isBetaEnabled(options, 'odataProxies') && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs)))
? getNewTypeName(node.type || artificialNameWitoutService(artificialName, service), !node.type)
isMultiSchema
? getNewTypeNameInMultiSchema(node.type || artificialNameWitoutService(artificialName, service), !node.type)
: `${service}.${newTypeId}`;

@@ -163,2 +175,3 @@

if (node.$location) setProp(newType, '$location', node.$location);
setProp(newType, '$exposedBy', 'typeExposure');

@@ -168,3 +181,3 @@ // Recurse into elements of 'type' (if any) and expose them as well (is needed)

if (node.elements && node.elements[elemName].$location) setProp(newType.elements[elemName], '$location', node.elements[elemName].$location);
exposeStructTypeOf(newType.elements[elemName], memberName, service, `${newTypeId}_${elemName}`, deleteElems, path);
exposeStructTypeOf(newType.elements[elemName], memberName, service, isMultiSchema ? `${newTypeFullName}_${elemName}` : `${newTypeId}_${elemName}`, deleteElems, path);
}

@@ -181,3 +194,3 @@ typeDef.kind === 'type' ? copyAnnotations(typeDef, newType) : copyAnnotations(node, newType);

* 2. When we have structured element (the object has property 'elements')
* @param {Object} node
* @param {Object} node
*/

@@ -195,13 +208,14 @@ function isExposableStructure(node) {

function getNewTypeName(typeName, isAnonym = false) {
if (isArtifactInSomeService(typeName, services) && !isAnonym) {
function getNewTypeNameInMultiSchema(typeName, isAnonym = false) {
if (isAnonym) {
const lastDotIdx = typeName.lastIndexOf('.');
const newContextName = lastDotIdx === -1 ? 'root' : typeName.substring(0, lastDotIdx);
if (!csn.definitions[`${newContextName}`])
csn.definitions[`${newContextName}`] = { kind: 'context' };
return `${lastDotIdx === -1 ? newContextName + '.' : ''}${typeName}`;
} else if (isArtifactInSomeService(typeName, services) && !isAnonym) {
// what is the name of the cross service references by the type
let crossServiceName = getServiceOfArtifact(typeName, services);
let typeWithoutServiceName = defNameWithoutServiceName(typeName, crossServiceName);
if (typeWithoutServiceName.startsWith('external.')) {
if (!csn.definitions[`${service}.external`])
csn.definitions[`${service}.external`] = { kind: 'context' };
return `${service}.${typeWithoutServiceName}`;
}
let crossServTypeDefName = `${service}.${crossServiceName}`;
const crossServiceName = getServiceOfArtifact(typeName, services);
const typeWithoutServiceName = defNameWithoutServiceName(typeName, crossServiceName);
const crossServTypeDefName = `${service}.${crossServiceName}`;
// is there such subContext already, if not -> create one

@@ -213,8 +227,12 @@ if (!csn.definitions[crossServTypeDefName])

} else {
let typeNamespace = csnUtils.getNamespaceOfArtifact(typeName);
let contextName = typeNamespace ?`${service}.${typeNamespace}` : `${service}.root`;
if (!csn.definitions[`${contextName}`])
csn.definitions[`${contextName}`] = { kind: 'context' };
const typeNamespace = csnUtils.getNamespaceOfArtifact(typeName);
const contextOfArt = csnUtils.getContextOfArtifact(typeName);
const newContextName = `${service}.${contextOfArt || typeNamespace || 'root'}`;
if (!csn.definitions[`${newContextName}`])
csn.definitions[`${newContextName}`] = { kind: 'context' };
// return the new type name
return `${contextName}.${nameWithoutNamespace(typeName).replace(/\./g, '_')}`;
return `${newContextName}.${contextOfArt ?
defNameWithoutServiceName(typeName, contextOfArt).replace(/\./g, '_')
: nameWithoutNamespace(typeName).replace(/\./g, '_')}`;
}

@@ -228,5 +246,5 @@ }

* 'parentName' is used for error reporting.x
* @param {String} typeName
* @param {Object} elements
* @param {String} parentName
* @param {String} typeName
* @param {Object} elements
* @param {String} parentName
*/

@@ -238,3 +256,3 @@ function exposeStructType(typeName, elements, parentName, path) {

if (!exposedStructTypes.includes(typeName)) {
signal(signal.error`Cannot create artificial type "${typeName}" for "${parentName}" because the name is already used`, path);
error(null, path, `Cannot create artificial type "${typeName}" for "${parentName}" because the name is already used`);
return null;

@@ -255,3 +273,3 @@ }

const path = ['definitions', typeName, 'elements', elemName];
signal(signal.error`"${elemName}": Element name conflicts with existing element`, path);
error(null, path, `"${elemName}": Element name conflicts with existing element`);
}

@@ -307,3 +325,3 @@ let cloned = cloneCsn(elements[elemName]);

if (!exposedStructTypes.includes(typeId)) {
signal(signal.error`Cannot create artificial type "${typeId}" because the name is already used`, newType.$path);
error(null, newType.$path, `Cannot create artificial type "${typeId}" because the name is already used`);
}

@@ -340,6 +358,8 @@ return newType;

function nameWithoutNamespace(name) {
let namespace = csnUtils.getNamespaceOfArtifact(name);
return name.replace(`${namespace}.`, '');
function nameWithoutNamespace(name) {
let namespace = csnUtils.getNamespaceOfArtifact(name);
return name.replace(`${namespace}.`, '');
}
}
module.exports = typesExposure;

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

let flatElem = cloneCsn(childElem);
// Don't take over notNull from leaf elements
delete flatElem.notNull;
setProp(flatElem, '$viaTransform', true); // FIXME: This name is not ideal but used elsewhere, too)

@@ -285,2 +287,3 @@ setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));

}
// Fix all collected flat elements (names, annotations, properties, origin ..)

@@ -292,3 +295,3 @@ for (let name in result) {

// Copy selected type properties
for (let p of ['key', 'notNull', 'virtual', 'masked', 'viaAll']) {
for (let p of ['key', 'virtual', 'masked', 'viaAll']) {
if (elem[p]) {

@@ -295,0 +298,0 @@ flatElem[p] = elem[p];

@@ -16,4 +16,17 @@ // Util functions for operations usually used with files.

/**
* Change Windows style line endings to Unix style
*
* @param {string} src
* @returns {string}
*/
function normalizeLineEndings(src) {
return (src && process.platform === 'win32') ? src.replace(/\r\n/g, '\n') : src;
}
module.exports = {
splitLines,
normalizeLineEndings,
};

@@ -24,2 +24,3 @@ // Custom resolve functionality for the CDS compiler

function resolveCDS(moduleName, options, callback) {
const isWindows = (process.platform === 'win32');
let resolvedBaseDir = path.resolve(options.basedir);

@@ -214,9 +215,17 @@

function nodeModulesPaths(absoluteStart) {
const parts = absoluteStart.split(path.sep); // TODO: Check if path is normalized on Windows
const dirs = []; // TODO: GLOBAL_FOLDERS
// Use platform-dependent separator. All NodeJS `path` methods use the system's path separator.
const parts = absoluteStart.split(path.sep);
// Do NOT use global node_modules directories.
const dirs = [];
// If we're on *nix systems, the first part is just an empty string ''
// because the path is absolute. Re-add it here because `path.join()`
// ignores empty segments which would result in a relative path.
if (!isWindows && parts.length > 0 && parts[0] === '')
parts[0] = '/';
for (let i = parts.length - 1; i >= 0; i--) {
if (parts[i] === 'node_modules')
continue;
const dir = path.join('/', ...parts.slice(0, i + 1), 'node_modules');
const dir = path.join(...parts.slice(0, i + 1), 'node_modules');
dirs.push(dir);

@@ -223,0 +232,0 @@ }

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

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

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

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

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

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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

Sorry, the diff of this file is 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

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