UNPKG

@eyugame/dao

Version:

A modelling tool for RealtimeDatabase(firebase) & Dynamodb(Amazon) & Redis

298 lines (265 loc) 7.51 kB
/** * DAO常量 & 工具 */ 'use strict'; const joi = require('joi'); const PLATFORM_FIREBASE = 'FIREBASE'; const PLATFORM_AWS = 'AWS'; const DB_REDIS = 'REDIS'; //supported databases const SUPPORTED_DBS = [PLATFORM_FIREBASE, PLATFORM_AWS, DB_REDIS] /** * 是否Firebase平台 */ function isFirebasePlatform() { return DB_PLATFORM === PLATFORM_FIREBASE; } /** * 是否AWS平台 */ function isAWSPlatform() { return DB_PLATFORM === PLATFORM_AWS; } /** * 是否Redis */ function isRedis() { return DB_PLATFORM === DB_REDIS; } /** * Schema对象 * 用于检查属性冲突 */ const SCHEMA_ATTRS = { 'hashKey': 'ALL', 'rangeKey': '', 'schema': 'ALL', 'tableName': '', 'ENTITY': '', 'FIREBASE_IGNORED': 'FIREBASE_IGNORED', 'STATIC_METHODS': '', 'INSTANCE_METHODS': '', 'BUILDER': '' }; /** * 实体属性 乐观锁字段 */ const ENTITY_ATTR_VERSION = '_v'; const SCHEMA_FIREBASE_IGNORED = 'FIREBASE_IGNORED'; const SCHEMA_BUILDER = 'builder'; const SCHEMA_INSTANCE_METHODS = 'methods'; /** * schema缺少属性异常 * @param {String} tableName * @param {String} attrName */ function throwAttrUndefined(tableName, attrName) { throw new Error(`实体${tableName}缺少${attrName}属性`); } /** * 检查schema * @param {String} entityName * @param {Object} schema */ function checkSchema(entityName, schema) { const table = schema.tableName || entityName; // // 增加JOI验证器 // if (!schema.validator) { // // 校验器增加版本号支持 // let tmpSchema = Object.assign({}, schema.schema); // tmpSchema[ENTITY_ATTR_VERSION] = joi.number().positive().optional(); // schema.validator = joi.object(tmpSchema); // } //检查schema 属性 for (let attr in SCHEMA_ATTRS) { let platformSupport = SCHEMA_ATTRS[attr]; if (platformSupport === 'ALL' && !schema[attr]) { throwAttrUndefined(table, attr); } else if (PLATFORM_FIREBASE === platformSupport) { //firebase必须的字段 if (!schema[attr]) { throwAttrUndefined(table, attr); } } else if (PLATFORM_AWS === platformSupport) { //aws必须的字段 if (!schema[attr]) { throwAttrUndefined(table, attr); } } } //检查实体属性 冲突 if (ENTITY_ATTR_VERSION in schema.schema) { throw new Error(`实体${table}, 属性${ENTITY_ATTR_VERSION}冲突`) } //todo:lg //检查 静态方法 冲突 //检查 对象方法 冲突 if (schema.INSTANCE_METHODS) { for (let funcName in schema.INSTANCE_METHODS) { } } } /** * 重写 entity getter,setter * @param entity * @returns {*} */ function wrapperEntity(entity) { //重写 get set const proxy = new Proxy(entity, { get: function (targetObj, propKey, receiver) { const originalAttr = targetObj[propKey]; if (typeof originalAttr === 'function') { return function (...args) { return originalAttr.apply(this, args); } } const propValue = targetObj._entity[propKey]; if (propValue === null || propValue === undefined) { return Reflect.get(targetObj, propKey, receiver); } if (DEBUG) { console.log(`实体${Object.getPrototypeOf(targetObj)._schema.tableName}访问属性${propKey}, 值${JSON.stringify(propValue)}`); } return propValue; }, set: function (targetObj, propKey, value, receiver) { const selfProto = Object.getPrototypeOf(targetObj) if (propKey in selfProto._schema.schema) { targetObj._entity[propKey] = value; return true; } if (propKey === '_entity') { targetObj._entity = value; return true; } console.log(`实体${selfProto._schema.tableName}非法的属性${propKey}赋值`); return false; } }); return proxy; } /** * 默认的构造器(静态方法) * @param {object} partitionKey */ function entityBuilder(partitionKey) { return { ...partitionKey }; } /** * 是否空对象 * @param o * @returns {boolean} */ function isEmptyObject(o) { for (let ignored in o) { return false; } return true; } /** * 获取数据类型 * @param o * @returns {string} */ function getType(o) { let s = Object.prototype.toString.call(o); return s.match(/\[object (.*?)\]/)[1].toLowerCase(); } /** * 是否数字类型 * @param o * @returns {boolean} */ function isNumber(o) { return getType(o) === 'number'; } /** * 是否异步函数 * @param {function} fn */ function isAsyncFunc(fn) { return '[object AsyncFunction]' === Object.prototype.toString.call(fn); } function initDB() { } /** * Set up db * @param {object} config */ function setupDB(config) { let targetDb = config.db; if (!targetDb) { throw new Error('Config参数错误, 无目标DB'); } targetDb = targetDb.toUpperCase(); if (!SUPPORTED_DBS.includes(targetDb)) { throw new Error(`Config参数错误, 目标DB: ${targetDb}不支持`); } global.DB_PLATFORM = targetDb; global.TABLE_PREFIX = config.tablePrefix || ''; console.log('--------------------'); console.log('DAO Platform:', DB_PLATFORM); console.log('TABLE Prefix:', TABLE_PREFIX); console.log('--------------------'); global.DAO_CONFIG = {}; if (isFirebasePlatform()) { DAO_CONFIG.rootPath = TABLE_PREFIX ? '/' + TABLE_PREFIX : '/'; } else if (isAWSPlatform()) { DAO_CONFIG.prefix = TABLE_PREFIX ? TABLE_PREFIX + '_' : ''; DAO_CONFIG.throughput = config.throughput || { readCapacity: 0, writeCapacity: 0 }; } else if (isRedis()) { DAO_CONFIG.connect = { host: config.host || 'localhost', port: config.port || 6379, } DAO_CONFIG.HASH_KEY_PREFIX = TABLE_PREFIX ? TABLE_PREFIX : ''; if (config.password && config.password.length > 0) { DAO_CONFIG.auth = { password: config.password } } } console.log('DAO set up success...'); } /** * 通用的entity => VO */ function toVO() { const result = {}; Object.assign(result, this._entity); delete result[ENTITY_ATTR_VERSION]; const selfProto = Object.getPrototypeOf(this); //const firebaseIgnoredAttrs = selfProto._schema.FIREBASE_IGNORED || selfProto._firebaseIgnored; const rangeKeyName = selfProto._schema.rangeKey; if (rangeKeyName) { delete result[rangeKeyName]; } else { delete result[selfProto._schema.hashKey]; } return result; } module.exports = { SCHEMA_ATTRS: SCHEMA_ATTRS, isFirebasePlatform: isFirebasePlatform, isAWSPlatform: isAWSPlatform, isRedis: isRedis, checkSchema: checkSchema, ENTITY_ATTR_VERSION: ENTITY_ATTR_VERSION, SCHEMA_FIREBASE_IGNORED: SCHEMA_FIREBASE_IGNORED, SCHEMA_BUILDER: SCHEMA_BUILDER, SCHEMA_INSTANCE_METHODS: SCHEMA_INSTANCE_METHODS, wrapperEntity: wrapperEntity, isEmptyObject: isEmptyObject, getType: getType, isNumber: isNumber, isAsyncFunc: isAsyncFunc, entityBuilder: entityBuilder, setupDB, toVO }