UNPKG

@odata2ts/odata2ts

Version:

Flexible generator to produce various TypeScript artefacts (from simple model interfaces to complete odata clients) from OData metadata files

239 lines 9.03 kB
import { ODataVersion, } from "./DataTypeModel.js"; export function withNamespace(ns, name) { // this supports the edge case of an empty string as namespace which isn't really valid according to spec (see CSDL) return ns ? `${ns}.${name}` : name; } export class DataModel { constructor(namespaces, version, converters = new Map()) { this.version = version; this.models = new Map(); /** * Stores unbound operations by their fully qualified name. * @private */ this.unboundOperationTypes = new Map(); /** * Stores operations bound to an entity type by the fully qualified name of the binding entity, e.g. * "Trippin.Person". * @private */ this.entityBoundOperationTypes = new Map(); /** * Stores operations bound to an entity collection by the fully qualified name of the binding entity, e.g. * "Trippin.Person". * @private */ this.entityCollectionBoundOperationTypes = new Map(); /** * Stores own type definitions which map to primitive types. * @private */ this.typeDefinitions = new Map(); this.aliases = {}; this.container = { entitySets: {}, singletons: {}, functions: {}, actions: {} }; this.converters = converters; this.namespace2Alias = namespaces.reduce((col, [ns, alias]) => { if (alias) { col[ns] = alias; } return col; }, {}); } /** * OData version: 2.0 or 4.0. * @returns */ getODataVersion() { return this.version; } isV2() { return this.version === ODataVersion.V2; } isV4() { return this.version === ODataVersion.V4; } retrieveType(fqName, haystack) { return haystack.get(fqName) || (this.aliases[fqName] ? haystack.get(this.aliases[fqName]) : undefined); } addAlias(namespace, name) { const alias = this.namespace2Alias[namespace]; if (alias) { this.aliases[withNamespace(alias, name)] = withNamespace(namespace, name); } } addTypeDefinition(namespace, name, type) { const fqName = withNamespace(namespace, name); this.typeDefinitions.set(fqName, type); this.addAlias(namespace, name); } getPrimitiveType(fqName) { return this.retrieveType(fqName, this.typeDefinitions); } getModel(fqName) { return this.retrieveType(fqName, this.models); } getModelTypes() { return [...this.models.values()]; } addEntityType(namespace, name, model) { const fqName = withNamespace(namespace, name); this.models.set(fqName, Object.assign(Object.assign({}, model), { dataType: "ModelType" /* DataTypes.ModelType */ })); this.addAlias(namespace, name); } /** * Get a specific model by its fully qualified name. * * @param fqName the fully qualified name of the entity * @returns the model type */ getEntityType(fqName) { return this.retrieveType(fqName, this.models); } /** * Retrieve all known EntityType models from the EDMX model. * * @returns list of model types */ getEntityTypes() { const ets = [...this.models.values()].filter((m) => m.dataType === "ModelType" /* DataTypes.ModelType */); return this.sortModelsByInheritance(ets); } addComplexType(namespace, name, model) { const fqName = withNamespace(namespace, name); this.models.set(fqName, Object.assign(Object.assign({}, model), { dataType: "ComplexType" /* DataTypes.ComplexType */ })); this.addAlias(namespace, name); } /** * Get a specific model by its fully qualified name. * * @param fqName the final model name that is generated * @returns the model type */ getComplexType(fqName) { return this.retrieveType(fqName, this.models); } /** * Retrieve all known ComplexType models from the EDMX model. * * @returns list of model types */ getComplexTypes() { const types = [...this.models.values()].filter((m) => m.dataType === "ComplexType" /* DataTypes.ComplexType */); return this.sortModelsByInheritance(types); } addEnum(namespace, name, type) { const fqName = withNamespace(namespace, name); this.models.set(fqName, Object.assign(Object.assign({}, type), { dataType: "EnumType" /* DataTypes.EnumType */ })); this.addAlias(namespace, name); } /** * Get list of all known enums, i.e. EnumType nodes from the EDMX model. * @returns list of enum types */ getEnums() { return [...this.models.values()].filter((m) => m.dataType === "EnumType" /* DataTypes.EnumType */); } addUnboundOperationType(namespace, operationType) { // supporting function overrides const isFunction = operationType.type === "Function" /* OperationTypes.Function */; const existingFn = isFunction ? this.unboundOperationTypes.get(operationType.fqName) : undefined; if (existingFn) { const params = operationType.parameters; existingFn.overrides ? existingFn.overrides.push(params) : (existingFn.overrides = [params]); return; } this.unboundOperationTypes.set(operationType.fqName, operationType); this.addAlias(namespace, operationType.odataName); } getUnboundOperationTypes() { return [...this.unboundOperationTypes.values()]; } getUnboundOperationType(fqOpName) { return this.retrieveType(fqOpName, this.unboundOperationTypes); } addBoundOperationType(namespace, bindingProp, operationType) { const fqEntityType = bindingProp.fqType; const store = bindingProp.isCollection ? this.entityCollectionBoundOperationTypes : this.entityBoundOperationTypes; const boundOps = store.get(fqEntityType); if (boundOps) { // supporting function overrides const isFunction = operationType.type === "Function" /* OperationTypes.Function */; const existingFn = isFunction ? boundOps.find((bo) => bo.fqName === operationType.fqName) : undefined; if (existingFn) { const params = operationType.parameters; existingFn.overrides ? existingFn.overrides.push(params) : (existingFn.overrides = [params]); } else { boundOps.push(operationType); } } else { store.set(fqEntityType, [operationType]); } } getEntityTypeOperations(fqEntityName) { const operations = this.retrieveType(fqEntityName, this.entityBoundOperationTypes); return operations || []; } getEntitySetOperations(fqEntityName) { const operations = this.retrieveType(fqEntityName, this.entityCollectionBoundOperationTypes); return operations || []; } getAllEntityOperations(fqEntityName) { return [...this.getEntityTypeOperations(fqEntityName), ...this.getEntitySetOperations(fqEntityName)]; } addAction(fqName, action) { this.container.actions[fqName] = action; } addFunction(fqName, func) { this.container.functions[fqName] = func; } addSingleton(fqName, singleton) { this.container.singletons[fqName] = singleton; } addEntitySet(fqName, entitySet) { this.container.entitySets[fqName] = entitySet; } getEntityContainer() { return this.container; } getConverter(dataType) { return this.converters.get(dataType); } sortModelsByInheritance(models) { // recursively visit all models and sort them by inheritance such that base classes // are always before derived classes const sorted = []; const visitedModels = new Set(); const inProgressModels = new Set(); function visit(model) { const fqName = model.fqName; if (inProgressModels.has(fqName)) { throw new Error(`Cyclic inheritance detected for model "${fqName}"!`); } if (!visitedModels.has(fqName)) { inProgressModels.add(fqName); for (const baseClassName of model.baseClasses) { const baseClass = models.find((e) => e.fqName === baseClassName); if (baseClass) { visit(baseClass); } } visitedModels.add(fqName); inProgressModels.delete(fqName); sorted.push(model); } } for (const model of models) { visit(model); } return sorted; } setNameValidation(map) { this.nameValidation = map; } getNameValidation() { return this.nameValidation; } } //# sourceMappingURL=DataModel.js.map