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