@odata2ts/odata2ts
Version:
Flexible generator to produce various TypeScript artefacts (from simple model interfaces to complete odata clients) from OData metadata files
158 lines • 6.81 kB
JavaScript
import { __rest } from "tslib";
import { TypeModel } from "../TypeModel.js";
import { withNamespace } from "./DataModel.js";
const EXCEPTION_NO_NAME = "No value for required attribute [name] specified!";
const EXCEPTION_WRONG_NAME_TYPE = "Wrong type for attribute [name]! You have to supply either a plain string or a RegExp.";
function createMapping() {
return {
names: {},
regExps: [],
};
}
export class ServiceConfigHelper {
constructor(options) {
this.propMapping = new Map();
this.propRegExps = [];
this.mapping = {
[TypeModel.Any]: createMapping(),
[TypeModel.EntityType]: createMapping(),
[TypeModel.ComplexType]: createMapping(),
[TypeModel.EnumType]: createMapping(),
[TypeModel.OperationType]: createMapping(),
[TypeModel.OperationImportType]: createMapping(),
[TypeModel.EntitySet]: createMapping(),
[TypeModel.Singleton]: createMapping(),
};
this.findPropConfigByName = (name) => {
const stringProp = this.getPropByName(name);
const reProp = this.getPropByRegExp(name);
return stringProp && reProp ? Object.assign(Object.assign({}, reProp), stringProp) : stringProp || reProp;
};
this.evaluateProps(options);
this.evaluateEntities(options);
}
evaluateProps(options) {
options.propertiesByName.forEach((prop) => {
if (!prop.name) {
throw new Error(EXCEPTION_NO_NAME);
}
switch (typeof prop.name) {
case "string":
// TODO: check for existing prop.name and throw?
this.propMapping.set(prop.name, prop);
break;
case "object":
const { source, ignoreCase } = prop.name;
if (!source) {
throw new Error(EXCEPTION_WRONG_NAME_TYPE);
}
this.propRegExps.push([new RegExp(`^${source}$`, ignoreCase ? "i" : ""), prop]);
break;
default:
throw new Error(EXCEPTION_WRONG_NAME_TYPE);
}
});
}
getPropByName(nameToMap) {
const stringProp = this.propMapping.get(nameToMap);
if (!stringProp) {
return undefined;
}
const { name } = stringProp, attrs = __rest(stringProp, ["name"]);
return Object.assign({}, attrs);
}
getPropByRegExp(nameToMap) {
const resultList = this.propRegExps
.filter(([regExp]) => regExp.test(nameToMap))
.map((_a) => {
var [regExp, _b] = _a, { name, mappedName } = _b, attrs = __rest(_b, ["name", "mappedName"]);
return (Object.assign({ mappedName: mappedName ? nameToMap.replace(regExp, mappedName) : undefined }, attrs));
});
return !resultList.length
? undefined
: resultList.reduce((result, prop) => {
return Object.assign(Object.assign({}, result), prop);
}, {});
}
evaluateEntities(options) {
options.byTypeAndName.forEach((ent) => {
if (!ent.name) {
throw new Error(EXCEPTION_NO_NAME);
}
const mapping = this.mapping[ent.type];
switch (typeof ent.name) {
case "string":
// TODO: check for existing prop.name and throw?
mapping.names[ent.name] = ent;
break;
case "object":
const { source, ignoreCase } = ent.name;
if (!source) {
throw new Error(EXCEPTION_WRONG_NAME_TYPE);
}
const regExp = new RegExp(`^${source}$`, ignoreCase ? "i" : "");
mapping.regExps.push(
// @ts-ignore: too generic
[regExp, ent]);
break;
default:
throw new Error(EXCEPTION_WRONG_NAME_TYPE);
}
});
}
// get entity config by name matching: simple name or fully qualified name (alias also supported)
getByName(mapping, namespace, nameToMap) {
const [ns, alias] = namespace;
const hayStack = Object.assign(Object.assign({}, this.mapping.Any.names), mapping);
const config = hayStack[withNamespace(ns, nameToMap)] ||
(alias ? hayStack[withNamespace(alias, nameToMap)] : undefined) ||
hayStack[nameToMap];
if (!config) {
return undefined;
}
const { name, type } = config, attrs = __rest(config, ["name", "type"]);
return Object.assign({}, attrs);
}
getByRegExp(mapping, [mainNs, alias], nameToMap) {
const fqName = `${mainNs}.${nameToMap}`;
const hayStack = [...this.mapping.Any.regExps, ...mapping];
const resultList = hayStack
.filter(([regExp, config]) => regExp.test(fqName))
.map((_a) => {
var [regExp, _b] = _a, { name, mappedName, type } = _b, attrs = __rest(_b, ["name", "mappedName", "type"]);
return (Object.assign({ mappedName: mappedName ? fqName.replace(regExp, mappedName) : undefined }, attrs));
});
return !resultList.length
? undefined
: resultList.reduce((result, prop) => {
return Object.assign(Object.assign({}, result), prop);
}, {});
}
findConfig(mapping, namespace, name) {
const stringEnt = this.getByName(mapping.names, namespace, name);
const reEnt = this.getByRegExp(mapping.regExps, namespace, name);
return stringEnt && reEnt ? Object.assign(Object.assign({}, reEnt), stringEnt) : stringEnt || reEnt;
}
findEntityTypeConfig(namespace, name) {
return this.findConfig(this.mapping.EntityType, namespace, name);
}
findComplexTypeConfig(namespace, name) {
return this.findConfig(this.mapping.ComplexType, namespace, name);
}
findEnumTypeConfig(namespace, name) {
return this.findConfig(this.mapping.EnumType, namespace, name);
}
findOperationTypeConfig(namespace, name) {
return this.findConfig(this.mapping.OperationType, namespace, name);
}
findOperationImportConfig(namespace, name) {
return this.findConfig(this.mapping.OperationImportType, [namespace], name);
}
findEntitySetConfig(namespace, name) {
return this.findConfig(this.mapping.EntitySet, [namespace], name);
}
findSingletonConfig(namespace, name) {
return this.findConfig(this.mapping.Singleton, [namespace], name);
}
}
//# sourceMappingURL=ServiceConfigHelper.js.map