vulcain-corejs
Version:
Vulcain micro-service framework
410 lines (408 loc) • 13.1 kB
JavaScript
"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