UNPKG

@sap/odata-v4

Version:

OData V4.0 server library

858 lines (718 loc) 26.1 kB
'use strict'; const KeyFactory = require('./KeyFactory'); const FullQualifiedName = require('../FullQualifiedName'); const IllegalCallError = require('../errors/IllegalCallError'); /** * Adds a function to an array which is container in the given map 'map' under the key 'key' * If the key is not in the map the map entries will be created * * @param {Map.<string, Array>} map Map * @param {string} key Key * @param {EdmFunction} ffunction Function to be added to the array in the map * @private */ function addToMapArray(map, key, ffunction) { let arr = map.get(key); if (!arr) { map.set(key, [ffunction]); } else { arr.push(ffunction); } } /** * The entity data model. */ class Edm { constructor() { /** * @type {Map.<string, EdmReference>} * @private */ this._references = null; /** * @type {Map.<string,EdmSchema>} * @private */ this._schemas = null; /** * @type {EdmSchema[]} * @private */ this._schemaList = null; /** * @type {Map.<?string,EdmEntityContainer>} * @private */ this._entityContainers = new Map(); /** * @type {Map.<string, EdmEnumType>} * @private */ this._enumTypes = new Map(); /** * @type {Map.<string,EdmTypeDefinition>} * @private */ this._typeDefinitions = new Map(); /** * @type {Map.<string,EdmEntityType>} * @private */ this._entityTypes = new Map(); /** * @type {Map.<string,EdmComplexType>} * @private */ this._complexTypes = new Map(); /** * @type {Map.<string,EdmAction>} * @private */ this._boundActions = new Map(); /** * @type {Map.<string,EdmAction>} * @private */ this._unboundActions = new Map(); /** * @type {Map.<string,EdmFunction[]>} * @private */ this._boundFunctionsByName = new Map(); /** * @type {Map.<string,EdmFunction>} * @private */ this._boundFunctionsByKey = new Map(); /** * @type {Map.<string,EdmFunction[]>} * @private */ this._unboundFunctionsByName = new Map(); /** * @type {Map.<string,EdmFunction>} * @private */ this._unboundFunctionByKey = new Map(); /** * @type {Map.<string,string>} * @private */ this._aliasToNamespaceInfo = null; /** * @type {Map.<string,EdmTerm>} * @private */ this._terms = new Map(); } /** * Return the references. * * @returns {Map.<string, EdmReference>} */ getReferences() { if (!this._references) { this.initReferences(); } return this._references; } /** * Return the schemas * * @returns {EdmSchema[]} */ getSchemas() { if (!this._schemaList) { this._initSchemas(); } return this._schemaList; } /** * Returns the schema given by namespace or alias, or null if schema is not found * * @param {string} namespace Namespace or alias for namespace * @returns {?EdmSchema} */ getSchema(namespace) { let schema; if (!this._schemas) { this._initSchemas(); } schema = this._schemas.get(namespace); if (!schema) { schema = this._schemas.get(this._aliasToNamespaceInfo.get(namespace)); } return schema; } /** * Initialize the {@link Edm#_schemas} map and {@link Edm#_schemaList} array * @private */ _initSchemas() { this.loadAliasToNamespaceInfo(); this._schemas = this.createSchemas(); this._schemaList = Array.from(this._schemas.values()); } /** * Fill the map containing the references */ initReferences() { this._references = this.createReferences(); } /** * Fill the map which links aliases to namespaces */ loadAliasToNamespaceInfo() { this._aliasToNamespaceInfo = this.createAliasToNamespaceInfo(); } /** * Return the entity container described via namespace or alias and name. * If namespaceOrAliasFQN is null the default entity container is returned. * If no container is found null is returned. * * @override * @param {FullQualifiedName} [namespaceOrAliasFQN] The namespace or alias and the name of the entity container to * be loaded * @returns {?EdmEntityContainer} */ getEntityContainer(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let container = this._entityContainers.get(fqnString); if (container) { return container; } container = this.createEntityContainer(fqn); if (container) { // fqnString may null if default container was requested this.cacheEntityContainer(fqn, container); if (fqn === null) { // if default container was requested store container also under his real name this.cacheEntityContainer(container.getFullQualifiedName(), container); } else { // we don't know if the by fqn requested container is the default container so // we look if the map contains already a default container let defaultContainer = this._entityContainers.get(null); if (!defaultContainer) { // if no default container is in the map add the default container // we ask the provider for the default container and defaultContainer = this.createEntityContainer(null); // if the default container is the currently requested container if (FullQualifiedName.equals(fqn, defaultContainer.getFullQualifiedName())) { // add the current container as default container this.cacheEntityContainer(null, container); } else { // add the default container with "null" and with his fqn this.cacheEntityContainer(null, defaultContainer); this.cacheEntityContainer(defaultContainer.getFullQualifiedName(), defaultContainer); } } } } return container; } /** * Return the enumeration type described via namespace or alias and name, or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {?EdmEnumType} */ getEnumType(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let enumType = this._enumTypes.get(fqnString); if (enumType) return enumType; enumType = this.createEnumType(fqn); if (enumType) this._enumTypes.set(fqnString, enumType); return enumType; } /** * Return the type definition described via namespace or alias and name, or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {?EdmTypeDefinition} */ getTypeDefinition(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let typeDefinition = this._typeDefinitions.get(fqnString); if (typeDefinition) { return typeDefinition; } typeDefinition = this.createTypeDefinition(fqn); if (typeDefinition) { this._typeDefinitions.set(fqnString, typeDefinition); } return typeDefinition; } /** * Return the entity type described via namespace or alias and name, or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {?EdmEntityType} */ getEntityType(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let entityType = this._entityTypes.get(fqnString); if (entityType) { return entityType; } entityType = this.createEntityType(fqn); if (entityType) { this._entityTypes.set(fqnString, entityType); } return entityType; } /** * Return the complex type described via namespace or alias and name, or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {?EdmComplexType} */ getComplexType(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let complexType = this._complexTypes.get(fqnString); if (complexType) { return complexType; } complexType = this.createComplexType(fqn); if (complexType) { this._complexTypes.set(fqnString, complexType); } return complexType; } /** * Return the unbound action described via namespace or alias and name, or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {?EdmAction} */ getUnboundAction(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let action = this._unboundActions.get(fqnString); if (action) { return action; } action = this.createUnboundAction(fqn); if (action) { this._unboundActions.set(fqnString, action); } return action; } /** * Return the bound action described via namespace or alias, name and binding information, * or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @param {FullQualifiedName} bindingParameterTypeName Name of the binding parameter * @param {boolean} isBindingParameterCollection True if the binding parameter is a collection, otherwise false * @returns {?EdmAction} */ getBoundAction(namespaceOrAliasFQN, bindingParameterTypeName, isBindingParameterCollection) { const actionFqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const bindingParameterTypeFqn = this.resolvePossibleAlias(bindingParameterTypeName); const key = KeyFactory.createActionKey( actionFqn, bindingParameterTypeFqn, isBindingParameterCollection); let action = this._boundActions.get(key); if (action) { return action; } action = this.createBoundAction( actionFqn, bindingParameterTypeFqn, isBindingParameterCollection); if (action) { this._boundActions.set(key, action); } return action; } /** * Return the unbound functions described via namespace or alias and name * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {EdmFunction[]} */ getUnboundFunctions(namespaceOrAliasFQN) { const functionFqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const functionFqnString = FullQualifiedName.getFQNAsString(functionFqn); let functions = this._unboundFunctionsByName.get(functionFqnString); if (!functions) { functions = this.createUnboundFunctions(functionFqn); this._unboundFunctionsByName.set(functionFqnString, functions); for (let unbound of functions) { // EdmFunction const key = KeyFactory.createFunctionKey( new FullQualifiedName(unbound.getNamespace(), unbound.getName()), unbound.getBindingParameterTypeFqn(), unbound.isBindingParameterTypeCollection(), Array.from(unbound.getParameters().keys())); this._unboundFunctionByKey.set(key, unbound); } } return functions; } /** * Return the bound functions described via namespace or alias and name * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {EdmFunction[]} */ getBoundFunctions(namespaceOrAliasFQN) { const functionFqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const functionFqnString = FullQualifiedName.getFQNAsString(functionFqn); let functions = this._boundFunctionsByName.get(functionFqnString); if (!functions) { functions = this.createBoundFunctions(functionFqn); this._boundFunctionsByName.set(functionFqnString, functions); for (let unbound of functions) { // EdmFunction const key = KeyFactory.createFunctionKey( new FullQualifiedName(unbound.getNamespace(), unbound.getName()), unbound.getBindingParameterTypeFqn(), unbound.isBindingParameterTypeCollection(), Array.from(unbound.getParameters().keys())); this._boundFunctionsByKey.set(key, unbound); } } return functions; } /** * Return the unbound function described via namespace or alias, name and parameter names, * or null if not found * * @override * @param {FullQualifiedName} functionName namespaceOrAliasFQN Namespace or alias and name * @param {string[]} parameterNames Parameter names * @returns {?EdmFunction} */ getUnboundFunction(functionName, parameterNames) { const functionFqn = this.resolvePossibleAlias(functionName); let key = KeyFactory.createFunctionKey(functionFqn, null, null, parameterNames); let unboundFunction = this._unboundFunctionByKey.get(key); if (!unboundFunction) { unboundFunction = this.createUnboundFunction(functionFqn, parameterNames); if (unboundFunction) { this._unboundFunctionByKey.set(key, unboundFunction); } } return unboundFunction; } /** * Return the bound action described via namespace or alias, name, binding information and parameter names * * @override * @param {FullQualifiedName} functionName namespaceOrAliasFQN Namespace or alias and name * @param {FullQualifiedName} bindingParameterTypeName Binding parameter name * @param {boolean} isBindingParameterCollection True if binding parameter is a collection, otherwise false * @param {string[]} parameterNames Parameter names * @returns {EdmFunction} */ getBoundFunction(functionName, bindingParameterTypeName, isBindingParameterCollection, parameterNames) { const functionFqn = this.resolvePossibleAlias(functionName); const bindingParameterTypeFqn = this.resolvePossibleAlias(bindingParameterTypeName); let key = KeyFactory.createFunctionKey(functionFqn, bindingParameterTypeFqn, isBindingParameterCollection, parameterNames); let ffunction = this._boundFunctionsByKey.get(key); if (!ffunction) { ffunction = this.createBoundFunction(functionFqn, bindingParameterTypeFqn, isBindingParameterCollection, parameterNames); if (ffunction != null) { this._boundFunctionsByKey.set(key, ffunction); } } return ffunction; } /** * Return the term described via namespace or alias and name, or null if not found * * @override * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {?EdmTerm} */ getTerm(namespaceOrAliasFQN) { const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN); const fqnString = FullQualifiedName.getFQNAsString(fqn); let term = this._terms.get(fqnString); if (term) { return term; } term = this.createTerm(fqn); if (term) { this._terms.set(fqnString, term); } return term; } /** * Resolves an alias of an namespace * null may be returned if the input parameter is null * * @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name * @returns {FullQualifiedName} */ resolvePossibleAlias(namespaceOrAliasFQN) { if (!this._aliasToNamespaceInfo) { this.loadAliasToNamespaceInfo(); } let finalFQN = null; // FullQualifiedName if (namespaceOrAliasFQN != null) { const namespace = this._aliasToNamespaceInfo.get(namespaceOrAliasFQN.namespace); // If not contained in info it must be a namespace if (!namespace) { finalFQN = namespaceOrAliasFQN; } else { finalFQN = new FullQualifiedName(namespace, namespaceOrAliasFQN.name); } } return finalFQN; } /** * Creates the schemas map * * @abstract * @protected * @returns {Map.<string, EdmSchema>} */ createSchemas() { throw IllegalCallError.createForCallingAbstractMethod('createSchemas'); } /** * Creates the references list * * @abstract * @protected * @returns {Map.<string, EdmReference>} */ createReferences() { throw IllegalCallError.createForCallingAbstractMethod('createReferences'); } /** * Creates a map to resolve aliases to namespaces * * @abstract * @protected * @returns {Map.<string, string>} */ createAliasToNamespaceInfo() { throw IllegalCallError.createForCallingAbstractMethod('createAliasToNamespaceInfo'); } /** * Stores a alias/namespaces tuple * * @param {string} alias Alias * @param {string} namespace Namespace * @private */ cacheAliasNamespaceInfo(alias, namespace) { if (!this._aliasToNamespaceInfo) { this.loadAliasToNamespaceInfo(); } this._aliasToNamespaceInfo.set(alias, namespace); } /** * Creates an entity container. * * @abstract * @protected * @param {FullQualifiedName} containerName Container name * @returns {EdmEntityContainer} */ // eslint-disable-next-line no-unused-vars createEntityContainer(containerName) { throw IllegalCallError.createForCallingAbstractMethod('createEntityContainer'); } /** * Caches an entity container. * * @param {FullQualifiedName} containerName Container name * @param {EdmEntityContainer} container Container * @private */ cacheEntityContainer(containerName, container) { const key = FullQualifiedName.getFQNAsString(containerName); this._entityContainers.set(key, container); } /** * Creates an enumeration type. * * @abstract * @protected * @param {FullQualifiedName} enumTypeName Name of the enumeration type * @returns {EdmEnumType} */ createEnumType(enumTypeName) { // eslint-disable-line no-unused-vars throw IllegalCallError.createForCallingAbstractMethod('createEnumType'); } /** * Creates a type definition. * * @abstract * @protected * @param {FullQualifiedName} typeDefinitionName Name of the type definition * @returns {EdmTypeDefinition} */ // eslint-disable-next-line no-unused-vars createTypeDefinition(typeDefinitionName) { throw IllegalCallError.createForCallingAbstractMethod('createTypeDefinition'); } /** * Creates an entity type. * * @abstract * @protected * @param {FullQualifiedName} entityTypeName Type name * @returns {EdmEntityType} */ // eslint-disable-next-line no-unused-vars createEntityType(entityTypeName) { throw IllegalCallError.createForCallingAbstractMethod('createEntityType'); } /** * Creates a complex type. * @abstract * @protected * @param {FullQualifiedName} complexTypeName Complex type name * @returns {EdmComplexType} */ // eslint-disable-next-line no-unused-vars createComplexType(complexTypeName) { throw IllegalCallError.createForCallingAbstractMethod('createComplexType'); } /** * Creates an unbound action * * @abstract * @protected * @param {FullQualifiedName} actionName Action name * @returns {EdmAction} */ // eslint-disable-next-line no-unused-vars createUnboundAction(actionName) { throw IllegalCallError.createForCallingAbstractMethod('createUnboundAction'); } /** * Creates all overloadings of an unbound function * * @abstract * @protected * @param {FullQualifiedName} functionName Function name * @returns {EdmFunction[]} */ // eslint-disable-next-line no-unused-vars createUnboundFunctions(functionName) { throw IllegalCallError.createForCallingAbstractMethod('createUnboundFunctions'); } /** * Creates a specific overloading of an unbound function * * @abstract * @protected * @param {FullQualifiedName} functionName Function name * @param {string[]} parameterNames Parameter names to identify the overloading * @returns {EdmFunction} */ // eslint-disable-next-line no-unused-vars createUnboundFunction(functionName, parameterNames) { throw IllegalCallError.createForCallingAbstractMethod('createUnboundFunction'); } /** * Creates a bound action * * @abstract * @protected * @param {FullQualifiedName} actionName Action name * @param {FullQualifiedName} bindingParameterTypeName Name of the binding parameter type * @param {boolean} isBindingParameterCollection True if the binding parameter is a collection, otherwise false * @returns {EdmAction} */ // eslint-disable-next-line no-unused-vars createBoundAction(actionName, bindingParameterTypeName, isBindingParameterCollection) { throw IllegalCallError.createForCallingAbstractMethod('createBoundAction'); } /** * Creates all overloadings of an bound function * * @abstract * @protected * @param {FullQualifiedName} functionName Function name * @returns {EdmFunction[]} */ // eslint-disable-next-line no-unused-vars createBoundFunctions(functionName) { throw IllegalCallError.createForCallingAbstractMethod('createBoundFunctions'); } /** * Creates a specific overloading of a bound function * * @abstract * @protected * @param {FullQualifiedName} functionName Function name * @param {FullQualifiedName} bindingParameterTypeName Name of the binding parameter type * @param {boolean} isBindingParameterCollection True if the binding parameter is a collection, otherwise false * @param {string[]} parameterNames Parameter names to identify the overloading * @returns {EdmFunction} */ // eslint-disable-next-line no-unused-vars createBoundFunction(functionName, bindingParameterTypeName, isBindingParameterCollection, parameterNames) { throw IllegalCallError.createForCallingAbstractMethod('createBoundFunction'); } /** * Creates a term * * @abstract * @protected * @param {FullQualifiedName} termName Term name * @returns {EdmTerm} */ // eslint-disable-next-line no-unused-vars createTerm(termName) { throw IllegalCallError.createForCallingAbstractMethod('createTerm'); } /** * Cache a function * * @param {FullQualifiedName} functionName Function name * @param {EdmFunction} ffunction * @private */ cacheFunction(functionName, ffunction) { const functionNameString = FullQualifiedName.getFQNAsString(functionName); const key = KeyFactory.createFunctionKey(functionName, ffunction.getBindingParameterTypeFqn(), ffunction.isBindingParameterTypeCollection(), Array.from(ffunction.getParameters().keys())); if (ffunction.isBound()) { this._boundFunctionsByKey.set(key, ffunction); addToMapArray(this._boundFunctionsByName, functionNameString, ffunction); } else { this._unboundFunctionByKey.set(key, ffunction); addToMapArray(this._unboundFunctionByKey, functionNameString, ffunction); } } /** * Cache an action * @param {FullQualifiedName} actionName Action name * @param {EdmAction} action * @private */ cacheAction(actionName, action) { const actionNameString = FullQualifiedName.getFQNAsString(actionName); if (action.isBound()) { const key = KeyFactory.createActionKey(actionName, action.getBindingParameterTypeFqn(), action.isBindingParameterTypeCollection()); this._boundActions.set(key, action); } else { this._unboundActions.set(actionNameString, action); } } } module.exports = Edm;