UNPKG

vulcain-corejs

Version:
410 lines (408 loc) 13.1 kB
"use strict"; const schemaBuilder_1 = require('./schemaBuilder'); const standards_1 = require('./standards'); const validator_1 = require('./validator'); const visitor_1 = require('./visitor'); const system_1 = require('./../configurations/globals/system'); /** * Schema definition */ class Schema { /** * Create a new schema * @param domain : current domain model * @param name : schema name or schema */ constructor(domain, name) { this.name = name; this._domain = domain; this.description = domain.findSchemaDescription(name); if (!this.description) { throw new Error(`Schema ${name} not found.`); } name = this.description.name; } /** * Current domain model * @returns {Domain} */ get domain() { return this._domain; } get extends() { if (!this.description.extends) { return null; } if (typeof this.description.extends === "string") { return this._domain.findSchemaDescription(this.description.extends); } else { return this.description.extends; } } /** * * @param origin * @returns {null|any|{}} */ bind(origin, old) { return this.domain.bind(origin, this.description, old); } validate(obj) { return this.domain.validate(obj, this.description); } getIdProperty() { return this.domain.getIdProperty(this.description); } getId(obj) { return obj[this.getIdProperty()]; } encrypt(entity) { if (!entity || !this.description.hasSensibleData) { return entity; } let visitor = { visitEntity(entity, schema) { this.current = entity; return schema.hasSensibleData; }, visitProperty(val, prop) { if (val && prop.sensible) { this.current[prop.name] = system_1.System.encrypt(val); } } }; let v = new visitor_1.SchemaVisitor(this.domain, visitor); v.visit(this.description, entity); return entity; } decrypt(entity) { if (!entity || !this.description.hasSensibleData) { return entity; } let visitor = { visitEntity(entity, schema) { this.current = entity; return schema.hasSensibleData; }, visitProperty(val, prop) { if (val && prop.sensible) { this.current[prop.name] = system_1.System.decrypt(val); } } }; let v = new visitor_1.SchemaVisitor(this.domain, visitor); v.visit(this.description, entity); return entity; } /** * Copy an entity to another taking into account references defined in schema * * @param {any} target * @param {any} source * @param {SchemaDescription} [schema] * @returns * * @memberOf Schema */ deepAssign(target, source, schema) { if (!source) { return target; } schema = schema || this.description; for (let key of Object.keys(source)) { let val = source[key]; if (typeof val === "object") { let ref = schema.references[key]; if (ref) { let item = ref.item; if (item === "any" && val && val.__schema) { item = val.__schema; } let elemSchema = this.domain.findSchemaDescription(item); if (elemSchema) { if (Array.isArray(val)) { target[key] = []; val.forEach(v => target[key].push(this.deepAssign({}, v, elemSchema))); } else { target[key] = this.deepAssign(target[key] || {}, val, elemSchema); } continue; } } } if (val !== undefined) { target[key] = val; } } return target; } } exports.Schema = Schema; /** * Domain model */ class Domain { constructor(name, container, defaultTypes) { this.name = name; this.container = container; this.builder = new schemaBuilder_1.SchemaBuilder(this); this._schemaDescriptions = new Map(); this.types = new Map(); this.types.set("", defaultTypes || standards_1.standards); } addSchemaDescription(schema, name) { let schemaName = null; if (Array.isArray(schema)) { schema.forEach(s => { let tmp = this.addSchemaDescription(s); if (!schemaName) { schemaName = tmp; } }); return schemaName; } if (!schema) { throw new Error("Invalid schema argument (null or empty) "); } if (typeof schema === "function") { let tmp = this._schemaDescriptions.get(schema.name); if (tmp) { return schema.name; } schema = this.builder.build(schema); } schemaName = name || schema.name; if (!schemaName) { return; } // Existing Model extension if (schema.extends === schema.name) { throw new Error("Invalid schema extension. Can not be the same schema."); } this._schemaDescriptions.set(schemaName, schema); return schemaName; } /** * Get a registered schema by name * Throws an exception if not exists * @param {string} schema name * @returns a schema */ getSchema(name) { if (typeof name === "string") { return new Schema(this, name); } return new Schema(this, this.addSchemaDescription(name)); } /** * Get all schemas * * @readonly */ get schemas() { return Array.from(this._schemaDescriptions.values()); } /** * Do not use directly * * @param {string} name * @returns */ findSchemaDescription(name) { return this._schemaDescriptions.get(name); } /** * Do not use directly * * @readonly */ get schemaDescriptions() { return Array.from(this._schemaDescriptions.values()); } addTypes(types, ns = "") { if (!types) { throw new Error("Invalid type argument"); } let old = this.types.get(ns); Object.assign(old || {}, types); this.types.set(ns, old); } addType(name, type, info, ns = "") { if (!name || !type) { throw new Error("Invalid type argument"); } let subType = this._findType(type); if (!subType) { throw new Error("Unknow type " + type); } let types = this.types.get(ns); if (!types) { types = {}; this.types.set(ns, types); } types[name] = schemaBuilder_1.SchemaBuilder.clone(subType, info); } _findType(name) { if (!name) { return null; } let parts = name.split('.'); if (parts.length === 1) { return this.types.get("")[name]; } if (parts.length !== 2) { throw new Error("Incorrect type name " + name); } return this.types.get(parts[0])[parts[1]]; } /** * Remove all sensible data * * @param {any} entity * @param {any} schemaName * @returns */ obfuscate(entity, schema) { let visitor = { visitEntity(entity, schema) { this.current = entity; return schema.hasSensibleData; }, visitProperty(val, prop) { if (prop.sensible) { delete this.current[prop.name]; } } }; let v = new visitor_1.SchemaVisitor(this, visitor); v.visit(schema.description, entity); } /** * Convert a new object from an other based on a specific schema * @param origin : initial object to convert * @param schemaName : schema to used (default=current schema) * @param obj : existing object to use * @returns {any} */ bind(origin, schemaName, obj) { if (!origin) { return null; } let schema = this.resolveSchemaDescription(schemaName, obj); if (typeof schema.bind === "function") { obj = schema.bind(origin); } obj = obj || origin; if (typeof obj !== "object") { return obj; } obj.__schema = obj.__schema || schema.name; // Convert properties for (const ps in schema.properties) { if (!schema.properties.hasOwnProperty(ps)) { continue; } let prop = schema.properties[ps]; if (prop) { try { let val = this.applyBinding(prop, origin, origin[ps]); if (val !== undefined) { obj[ps] = val; } else if (prop.defaultValue !== undefined) { obj[ps] = prop.defaultValue; } } catch (e) { } } } for (const ref in schema.references) { if (!schema.references.hasOwnProperty(ref)) { continue; } let relationshipSchema = schema.references[ref]; let refValue = origin[ref]; if (relationshipSchema && refValue) { try { let item = relationshipSchema.item; if (item === "any" && refValue && refValue.__schema) { item = refValue.__schema; } let elemSchema = this.findSchemaDescription(item); if (!elemSchema && item !== "any") { continue; } if (this.isMany(relationshipSchema)) { obj[ref] = []; for (let elem of refValue) { let val = this.applyBinding(relationshipSchema, elemSchema, elem); if (val !== undefined) { obj[ref].push(!elemSchema && item === "any" ? val : this.bind(val, elemSchema)); } } } else { let val = this.applyBinding(relationshipSchema, elemSchema, refValue); if (val !== undefined) { obj[ref] = !elemSchema && item === "any" ? val : this.bind(val, elemSchema); } } } catch (e) { } } } if (schema.extends) { this.bind(origin, schema.extends, obj); } return obj; } applyBinding(prop, origin, val) { let mainTypeValidator = prop.validators[prop.validators.length - 1]; if (mainTypeValidator["bind"] === false) { return undefined; } // skip value for (let validator of prop.validators) { let convert = validator["bind"]; if (convert === false) { continue; } if (convert && typeof convert === "function") { val = convert.apply(prop, [val, origin]); } } return val; } isMany(relSchema) { return relSchema.cardinality === "many"; } /** * Validate an object * @param val : Object to validate * @param schemaName : schema to use (default=current schema) * @returns Array<string> : A list of errors */ validate(val, schemaName) { if (!val) { return []; } let schema = this.resolveSchemaDescription(schemaName, val); var validator = new validator_1.Validator(this, this.container); return validator.validate(schema, val); } resolveSchemaDescription(schemaName, val) { let schema; if (!schemaName || typeof schemaName === "string") { schemaName = schemaName || val && val.__schema; schema = this._schemaDescriptions.get(schemaName); if (!schema) { throw new Error("Unknown schema " + schemaName); } } else { schema = schemaName; if (!schema) { throw new Error("Invalid schema"); } } return schema; } getIdProperty(schemaName) { let schema = this.resolveSchemaDescription(schemaName); return schema.idProperty; } } exports.Domain = Domain; //# sourceMappingURL=schema.js.map