airship-server
Version:
Airship is a framework for Node.JS & TypeScript that helps you to write big, scalable and maintainable API servers.
176 lines • 8.03 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const ClassScheme_1 = require("../codeGen/domain/schema/ClassScheme");
const ClassField_1 = require("../codeGen/domain/schema/ClassField");
const StringType_1 = require("../codeGen/domain/types/StringType");
const NumberType_1 = require("../codeGen/domain/types/NumberType");
const BooleanType_1 = require("../codeGen/domain/types/BooleanType");
const CustomType_1 = require("../codeGen/domain/types/CustomType");
const VectorType_1 = require("../codeGen/domain/types/VectorType");
require("reflect-metadata");
const AnyType_1 = require("../codeGen/domain/types/AnyType");
const ObjectType_1 = require("../codeGen/domain/types/ObjectType");
const SerializableKey = 'SerializableKey';
const SerializableKeys = 'SerializableKeys';
const SerializableTypes = 'SerializableTypes';
const SerializableArrayTypes = 'SerializableArrayTypes';
function isSerializableObject(object) {
if (object == 'any')
return false;
return !!object.prototype[SerializableKeys];
}
function json(name, arrayType, isAny = false) {
return (target, propertyKey) => {
const constructor = target;
name = name || propertyKey.replace('_', '');
Reflect.defineMetadata(SerializableKey, name, target, propertyKey);
constructor[SerializableKeys] = constructor[SerializableKeys] || [];
constructor[SerializableTypes] = constructor[SerializableTypes] || [];
constructor[SerializableArrayTypes] = constructor[SerializableArrayTypes] || [];
let type = Reflect.getMetadata('design:type', target, propertyKey);
// we need new arrays here, because at this point they are references to super class arrays
// so just pushing new values would modify super (and other child) class array
constructor[SerializableKeys] = [...constructor[SerializableKeys], name];
constructor[SerializableTypes] = [...constructor[SerializableTypes], (isAny ? 'any' : type)];
constructor[SerializableArrayTypes] = [...constructor[SerializableArrayTypes], arrayType];
};
}
exports.json = json;
function deserialize(serializableType, raw, valuePath = []) {
const serializableConstructor = serializableType.prototype;
const propsNames = serializableConstructor[SerializableKeys] || [];
const types = serializableConstructor[SerializableTypes];
const arrayTypes = serializableConstructor[SerializableArrayTypes];
const props = propsNames.map((prop, index) => {
let type = types[index];
let arrayType = arrayTypes[index];
let isSerializable = isSerializableObject(type) == true;
valuePath.push(prop);
checkType(valuePath.join('.'), raw[prop], type, isSerializable);
if (arrayType &&
isSerializableObject(arrayType) == true) {
let val = raw[prop].map((v, i) => {
valuePath.push(`{${i}}`);
let deserialized = deserialize(arrayType, v, valuePath);
valuePath.pop();
return deserialized;
});
valuePath.pop();
return val;
}
else if (arrayType) {
let val = raw[prop].map((v, i) => {
valuePath.push(`{${i}}`);
checkType(valuePath.join('.'), v, arrayType, false);
valuePath.pop();
return v;
});
valuePath.pop();
return val;
}
else if (isSerializable) {
let val = deserialize(type, raw[prop], valuePath);
valuePath.pop();
return val;
}
else {
valuePath.pop();
return raw[prop];
}
});
return new serializableConstructor.constructor(...props);
}
exports.deserialize = deserialize;
function checkType(prop, value, expectedType, isSerializable) {
if (value == undefined)
throw new Error(`${prop} argument missing`);
let valueType = typeof value;
if (expectedType == Array && !Array.isArray(value))
throw new Error(`${prop} must be array`);
if (!isSerializable) {
if (expectedType == String && valueType !== 'string')
throw new Error(`${prop} must be string instead of ${valueType}`);
if (expectedType == Number && valueType !== 'number')
throw new Error(`${prop} must be number instead of ${valueType}`);
if (expectedType == Boolean && valueType !== 'boolean')
throw new Error(`${prop} must be boolean instead of ${valueType}`);
if (expectedType == Object && valueType !== 'object')
throw new Error(`${prop} must be boolean instead of ${valueType}`);
}
}
function serialize(entity) {
let result = {};
let arrayTypes = entity[SerializableArrayTypes];
let i = 0;
for (let prop in entity) {
if (prop == SerializableKeys ||
prop == SerializableTypes ||
prop == SerializableArrayTypes)
continue;
let keyName = Reflect.getMetadata(SerializableKey, entity, prop) || prop;
let type = Reflect.getMetadata('design:type', entity, prop);
let arrayType = arrayTypes[i];
if (arrayType && isSerializableObject(arrayType) == true) {
result[keyName] = entity[prop].map(serialize);
}
else if (isSerializableObject(type) == true) {
result[keyName] = serialize(entity[prop]);
}
else
result[keyName] = entity[prop];
i++;
}
return result;
}
exports.serialize = serialize;
function getClassScheme(serializableType) {
const serializableConstructor = serializableType.prototype;
let propsNames = serializableConstructor[SerializableKeys];
const types = serializableConstructor[SerializableTypes];
const arrayTypes = serializableConstructor[SerializableArrayTypes];
let fields = [];
propsNames = propsNames || [];
propsNames.forEach((propName, index) => {
fields.push(new ClassField_1.default(propName, getType(types[index], arrayTypes[index]), ''));
});
return new ClassScheme_1.default(serializableConstructor.constructor.name, fields);
}
exports.getClassScheme = getClassScheme;
function getType(propType, arrayType) {
if (propType == String)
return new StringType_1.default();
if (propType == Number)
return new NumberType_1.default();
if (propType == Boolean)
return new BooleanType_1.default();
if (propType == Object)
return new ObjectType_1.default();
if (propType == 'any')
return new AnyType_1.default();
if (isSerializableObject(propType) == true)
return new CustomType_1.default(propType.prototype.constructor.name);
if (propType == Array && arrayType)
return new VectorType_1.default(getType(arrayType));
throw new Error(`Model scheme generation fail, unknown type: ${propType}`);
}
function getClassDependencies(serializableType) {
const serializableConstructor = serializableType.prototype;
const propsNames = serializableConstructor[SerializableKeys] || [];
const types = serializableConstructor[SerializableTypes];
const arrayTypes = serializableConstructor[SerializableArrayTypes];
let dependencies = {};
let subDeps = [];
propsNames.forEach((propName, index) => {
if (isSerializableObject(types[index]) == true) {
dependencies[types[index].prototype.constructor.name] = types[index];
subDeps = [...subDeps, ...getClassDependencies(types[index])];
}
else if (arrayTypes[index] && isSerializableObject(arrayTypes[index]) == true) {
dependencies[arrayTypes[index].prototype.constructor.name] = arrayTypes[index];
subDeps = [...subDeps, ...getClassDependencies(arrayTypes[index])];
}
});
return [...Object.keys(dependencies).map(key => dependencies[key]), ...subDeps];
}
exports.getClassDependencies = getClassDependencies;
//# sourceMappingURL=Serialize.js.map