@sap/odata-v4
Version:
OData V4.0 server library
123 lines (108 loc) • 4.73 kB
JavaScript
;
const UriSemanticError = require('../errors/UriSemanticError');
const KeyValueParser = require('./KeyValueParser');
/**
* Parsing uri key predicates.
*
* @extends KeyValueParser
*/
class KeyPredicateParser extends KeyValueParser {
/**
* Creates an instance of KeyPredicateParser.
*
* @param {Edm} edm The Entity Data Model
* @param {Object} aliases Alias definitions
*/
constructor(edm, aliases) {
super(edm, aliases);
}
/**
* Parsing the next key predicates.
*
* @param {UriResource} currentResource The current resource
* @param {EdmEntityType} edmEntityType The EdmType of the current resource
* @param {UriTokenizer} tokenizer The current uri tokenizer
* @returns {UriParameter[]} a map of found parameters
*/
parse(currentResource, edmEntityType, tokenizer) {
const requiredKeyProperties = this._buildRequiredKeyProperties(currentResource, edmEntityType);
return [].concat(
requiredKeyProperties.size > 1 ?
this._parseCompoundKey(requiredKeyProperties, edmEntityType, tokenizer) :
this._parseSimpleKey(requiredKeyProperties, edmEntityType, tokenizer)
);
}
/**
* Builds the required key properties map.
*
* @param {UriResource} currentResource The current resource
* @param {EdmEntityType} edmEntityType The edm entity type of current resource
* @returns {Map.<string, EdmKeyPropertyRef>} the map of required key properties
* @private
*/
_buildRequiredKeyProperties(currentResource, edmEntityType) {
let requiredKeyProperties = new Map(edmEntityType.getKeyPropertyRefs());
const navigationProperty = currentResource.getNavigationProperty();
if (navigationProperty) {
const partner = navigationProperty.getPartner();
if (partner) {
// "If a navigation property leading to a related entity type has a
// partner navigation property that specifies a referential constraint,
// then those key properties of the related entity that take part in
// the referential constraint MAY be omitted from URLs."
for (const name of partner.getReferentialConstraints().keys()) {
requiredKeyProperties.delete(name);
}
}
}
return requiredKeyProperties;
}
/**
* Parsing a compound key.
*
* @param {string[]} requiredKeyProperties The required key property names
* @param {EdmEntityType} edmEntityType The current edm entity type
* @param {UriTokenizer} tokenizer The current tokenizer
* @returns {UriParameter[]} The found uri parameters
* @private
*/
_parseCompoundKey(requiredKeyProperties, edmEntityType, tokenizer) {
const visitedKeyProperties = super.parse(tokenizer,
propertyRefName => edmEntityType.getKeyPropertyRef(propertyRefName),
propertyRefType => [propertyRefType.getProperty().getType()]
);
// Testing if at least all required key properties have been visited
const missingKeyPredicateNames = Array.from(requiredKeyProperties.keys())
.filter(name => !visitedKeyProperties.has(name));
if (missingKeyPredicateNames.length > 0) {
throw new UriSemanticError(UriSemanticError.Message.KEY_PREDICATES_MISSING,
missingKeyPredicateNames.join(','));
}
let result = [];
for (let [key, value] of visitedKeyProperties) {
result.push(value.setEdmRef(edmEntityType.getKeyPropertyRef(key)));
}
return result;
}
/**
* Parsing a simple key.
*
* @param {string[]} requiredKeyProperties The required key property names
* @param {EdmEntityType} edmEntityType The current edm entity type
* @param {UriTokenizer} tokenizer The current tokenizer
* @returns {UriParameter[]} The found uri parameter in an array
* @private
*/
_parseSimpleKey(requiredKeyProperties, edmEntityType, tokenizer) {
const propertyRef = requiredKeyProperties.values().next().value;
const propertyRefType = propertyRef.getProperty().getType();
// First try to parse key when key is like ES(value)
let parameter = this.parseUriParameter(propertyRefType, tokenizer);
if (parameter) {
return [parameter.setEdmRef(propertyRef)];
}
// Second try to parse key when key is like ES(ODataIdentifier=value)
return this._parseCompoundKey(requiredKeyProperties, edmEntityType, tokenizer);
}
}
module.exports = KeyPredicateParser;