@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
161 lines (148 loc) • 5.64 kB
JavaScript
;
const lodash = require('lodash');
/**
* Exports the api
*/
module.exports = {
validate: validate,
validateHasNotKeyPropertyToken: validateHasNotKeyPropertyTokenKey,
};
/**
* Validates the given context containing xsodata configuration with concurrencytokens.
* The validation checks all entity types concurrencytokens.
*
* @param context {object} The context object to validate
* @param callback (function) The function called on validation. Expected signature is
* callback(err, context)
*/
function validate(context, callback) {
const entityTypes = context.gModel._entityTypesMap;
try {
validateDefaults(entityTypes);
validateHasNotNavigationPropertyTokenKey(entityTypes);
validateHasNotKeyPropertyTokenKey(entityTypes);
callback(null, context);
} catch (err) {
callback(err, context);
}
}
/**
* Validates that the entity types concurrenytoken does not have an 'empty' property.
*
* @param {object} entityTypes The entityTypes collection to validate
* @throws {Error} When concurrencytoken has an empty property
*/
function validateDefaults(entityTypes) {
findConcurrencyTokenConflicts(entityTypes, null, function (entityType) {
if (entityType.getConcurrentPropertiesMap()['']) {
throw new Error(
"XSODATA configuration has invalid concurrencytoken '' " +
'for entity ' +
entityType.name +
'. An empty property is not allowed'
);
}
if (entityType._aggregates && entityType.hasConcurrencyToken()) {
throw new Error(
'XSODATA configuration is invalid. Usage of aggregation' +
' and concurrencytoken in same entity definition is not allowed'
);
}
});
}
/**
* Validates that the entity types concurrenytoken does not have a property already defined
* as a navigation property.
*
* @param {object} entityTypes The entityTypes collection to validate
* @throws {Error} When concurrencytoken property is a navigation property
*/
function validateHasNotNavigationPropertyTokenKey(entityTypes) {
findConcurrencyTokenConflicts(
entityTypes,
'_entityType.navigates',
function (entityType, intersection) {
if (intersection.length > 0) {
throw new Error(
"XSODATA configuration has invalid concurrencytoken '" +
intersection.join(',') +
"' for entity " +
entityType.name +
'. A navigation property is not allowed'
);
}
}
);
}
/**
* Validates that the entity types concurrenytoken does not have a property already defined
* as a key or generated key property.
*
* @param {object} entityTypes The entityTypes collection to validate
* @throws {Error} When concurrencytoken property is a key or generated key property
*/
function validateHasNotKeyPropertyTokenKey(entityTypes) {
findConcurrencyTokenConflicts(
entityTypes,
'keys.names',
function (entityType, intersection) {
const isInvalidKey =
entityType.hasConcurrentProperties() &&
entityType.isConcurrentProperty(entityType.keys.generatedKey);
if (isInvalidKey) {
throw new Error(
"XSODATA configuration has invalid concurrencytoken '" +
entityType.keys.generatedKey +
"' for entity " +
entityType.name +
'. A key property is not allowed'
);
}
if (intersection.length > 0) {
throw new Error(
"XSODATA configuration has invalid concurrencytoken '" +
intersection.join(',') +
"' for entity " +
entityType.name +
'. A key property is not allowed'
);
}
}
);
}
/**
* Iterates over provided entityTypes collection. Returns an intersection between
* concurrent properties and properties identified by comparableChildPropertyPath parameter.
*
* @param entityTypes {object} The collection of entityTypes
* @param comparableChildPropertyPath {string} Path to comparable object inside entityTypes object
* @param callback {function} Function called on each value found in entityTypes.
* Function signature is callback(entityType, intersection) where intersection is an array
* of already found key intersections.
* @return {array} Intersection of keys of comparables;
*/
function findConcurrencyTokenConflicts(
entityTypes,
comparableChildPropertyPath,
callback
) {
return lodash
.chain(entityTypes)
.reduce(function (result, entityType) {
let comparableKeys;
let intersection;
let tokenKeys;
comparableKeys = lodash
.chain(entityType)
.get(comparableChildPropertyPath)
.value();
if (comparableKeys && !Array.isArray(comparableKeys)) {
comparableKeys = lodash.keys(comparableKeys);
}
tokenKeys = entityType.getConcurrentProperties();
intersection = lodash.intersection(comparableKeys, tokenKeys);
callback(entityType, intersection);
return result.concat(intersection);
}, [])
.value();
}