@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
JavaScript
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