@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
138 lines (114 loc) • 5.07 kB
JavaScript
;
var 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) {
var 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) {
var 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) {
var comparableKeys;
var intersection;
var 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();
}