vulcain-corejs
Version:
Vulcain micro-service framework
126 lines (124 loc) • 5.14 kB
JavaScript
require("reflect-metadata");
class SchemaBuilder {
constructor(domain) {
this.domain = domain;
}
build(model) {
if (!model)
return null;
let schema = { name: model.name, properties: {}, references: {} };
const symModel = Symbol.for("design:model");
let modelAttr = Reflect.getMetadata(symModel, model);
if (modelAttr) {
for (let n of Object.keys(modelAttr)) {
let p = modelAttr[n];
if (typeof p === "function")
schema[n] = p.bind(schema);
else {
schema[n] = p;
}
}
}
schema.storageName = schema.storageName || schema.name;
const symProperties = Symbol.for("design:properties");
let properties = Reflect.getOwnMetadata(symProperties, model.prototype);
for (let propertyName in properties) {
let propAttr = properties[propertyName];
if (propAttr) {
if (propAttr.type !== "any" && !this.domain._findType(propAttr.type))
throw new Error(`Unknown type ${propAttr.type} for property ${propertyName} of schema ${schema.name}`);
if (propAttr.isKey) {
if (schema.idProperty)
throw new Error("Multiple property id is not valid for schema " + schema.name);
schema.idProperty = propertyName;
}
if (schema.hasSensibleData === undefined) {
if (propAttr.sensible)
schema.hasSensibleData = true;
}
schema.properties[propertyName] = propAttr;
propAttr.validators = this.createValidatorsChain(propAttr.type, propAttr, propertyName, model);
}
}
if (!schema.idProperty) {
if (schema.properties["id"])
schema.idProperty = "id";
else if (schema.properties["name"])
schema.idProperty = "name";
}
const symReferences = Symbol.for("design:references");
let references = Reflect.getOwnMetadata(symReferences, model.prototype);
for (let referenceName in references) {
let refAttr = references[referenceName];
if (refAttr) {
let itemSchema;
if (refAttr.item !== "any") {
itemSchema = this.domain.findSchemaDescription(refAttr.item);
if (!itemSchema)
throw new Error(`Unknown referenced schema ${refAttr.item} for reference ${referenceName} of schema ${schema.name}`);
}
schema.references[referenceName] = refAttr;
refAttr.validators = this.createValidatorsChain(refAttr.type || "$ref", refAttr, referenceName, model);
if (schema.hasSensibleData === undefined && itemSchema) {
if (itemSchema.hasSensibleData)
schema.hasSensibleData = true;
}
}
}
return schema;
}
createValidatorsChain(typeName, attributeInfo, propertyName, obj) {
let chain = [];
const symValidators = Symbol.for("design:validators");
let type = this.domain._findType(typeName);
if (type) {
let clonedType = SchemaBuilder.clone(type, attributeInfo);
for (let fn of ["bind", "dependsOn", "validate"]) {
if (attributeInfo[fn]) {
clonedType[fn] = attributeInfo[fn];
}
}
// Type inheritence (in reverse order)
let stypeName = type.type;
while (stypeName) {
let stype = this.domain._findType(stypeName);
if (!stype)
break;
chain.unshift(SchemaBuilder.clone(stype, attributeInfo));
stypeName = stype.type;
}
// Then type
chain.push(clonedType);
}
// And validator
let validators = Reflect.getOwnMetadata(symValidators, obj.prototype, propertyName);
if (validators) {
for (let { name, info } of validators) {
let validator = this.domain._findType(name);
if (!validator)
throw new Error(`Unknow validator ${name}`);
else
chain.push(SchemaBuilder.clone(validator, info));
}
}
return chain;
}
// Copy all schema property names from 'from'
// TODO use extends
static clone(schema, from) {
let clone = {};
for (let key of Object.keys(schema)) {
if (key && key[0] === "$") {
let pname = key.substr(1);
clone[key] = (from && from[pname]) || schema[key];
}
else if (key !== "validators") {
clone[key] = schema[key];
}
}
return clone;
}
}
exports.SchemaBuilder = SchemaBuilder;
//# sourceMappingURL=schemaBuilder.js.map
;