sails-hook-blocks
Version:
A Sails Micro-app architecture framework
451 lines (434 loc) • 12.8 kB
JavaScript
import Joi from 'joi';
import Validator from 'validator';
import _ from 'lodash';
import jsonValidator from './lib/jsonValidator';
const vAllowSymbol = (payload, symbols) => {
const rule = `^[${symbols.reduce((f, r) => `${f}${r}`)}]*$`;
// console.log('symbols.rule=>', rule);
return payload.match(rule);
};
const theValidator = {
/**
* 傳入參數以檢查是否存在該筆資料
* @deprecated 使用 isModelColumn 取代
* @param {*} payload
* @returns
*/
async checkNullData(payload) {
return this.isModelColumn(payload);
},
/**
* 傳入參數以檢查是否存在該筆資料
* @deprecated 使用 isModelColumn 取代
* @param {*} payload
* @returns
*/
async isNotExists(payload) {
return this.isModelColumn(payload);
},
/**
* 參數需傳入{}形式,表示其中一個不為 null 即可
* @export
* @deprecated 使用 checkInput 取代
* @param {array} payload
* @returns result
*/
checkNull(payload, options) {
return this.checkInput(payload, options);
},
/**
* 傳入參數以檢查是否存在該筆資料
*
* @param {array} payload:[[Model, columnName, value]]
* @returns {boolean} verification result
*/
async isModelColumn(payload) {
if (payload.log) {
sails.log.info('VerifyHelper isModelColumn payload=>\n', payload);
}
try {
if (!payload) {
throw Error(MESSAGE.BAD_REQUEST.NO_REQUIRED_PARAMETER({ payload }));
}
if (!_.isArray(payload)) {
throw Error(MESSAGE.BAD_REQUEST.CHECK_INPUT_PARAMETER_TYPE({ payload: 'Array' }));
}
let itemShouldExists;
for (const item of payload) {
if (item.length !== 3) {
// eslint-disable-next-line
continue;
}
// console.log('item[0]=>', item[0].name);
if (!item[0]) {
throw Error(MESSAGE.BAD_REQUEST.NO_REQUIRED_PARAMETER({
message: '1st parameter: model or model name',
}));
}
const modelName = item[0].name || item[0];
const model = sails.models[modelName.toLowerCase()];
// console.log('model=>', model);
if (!model) {
throw Error(MESSAGE.BAD_REQUEST.MODEL_NOT_EXISTS({ model }));
}
const key = item[1];
const value = item[2];
const where = {
[key]: value,
};
// eslint-disable-next-line
itemShouldExists = await model.findOne({
where,
attributes: ['id'],
});
if (!itemShouldExists) {
return `${model.name}.${key}=${value}`;
}
}
return false;
} catch (e) {
throw e;
}
},
/**
* 參數需傳入{}形式,表示其中一個不為 null
* @param {object} payload
* @param {object} [{
* allowNull = true,
* allowUndefined = true,
* allowSymbols = null,
* assertType = null,
* }={}]
* @returns {boolean} verification result
*/
checkInput(payload, {
allowNull = false,
allowUndefined = false,
allowSymbols = null,
assertType = null,
} = {}) {
// sails.log.debug('VerifyHelper checkInput payload=>\n', payload);
try {
if (!payload) {
throw Error(MESSAGE.BAD_REQUEST.NO_REQUIRED_PARAMETER({ payload }));
}
if (!_.isObject(payload)) {
throw Error(MESSAGE.BAD_REQUEST.PARAMETER_FORMAT_INVALID({ payload: typeof payload }));
}
// console.log('Object.keys(payload)=>', Object.keys(payload));
const nullItemIndex = Object
.keys(payload)
.findIndex((e) => {
// console.log('payload[e]=>', payload[e]);
const value = payload[e];
// 檢查 null
if (!allowNull && _.isNull(value)) {
return true;
}
// 檢查 Undefined
if (!allowUndefined && _.isUndefined(value)) {
return true;
}
// 檢查 array 內容
if (!_.isNil(value) && _.isArray(value)) {
return !value.some(i => !_.isNil(i));
}
// 如果不是 Undefined/null,則檢查型別是否符合
if (!_.isNil(value)
&& _.isString(assertType)) {
// eslint-disable-next-line valid-typeof
return typeof value !== assertType.toLowerCase();
}
// 檢查特殊字元
if (allowSymbols) {
const symbols = allowSymbols;
return vAllowSymbol(payload, symbols);
}
return false;
});
return (nullItemIndex === -1)
? null
: Object.keys(payload)[nullItemIndex];
} catch (err) {
throw err;
}
},
/**
* 檢查輸入的 payload 是否全部都是純數字
* @param {string|number} payload
* @returns {boolean} verification result
*/
isNotNumeric(payload, {
allowNull = false,
allowUndefined = false,
} = {}) {
sails.log.debug('VerifyHelper isNumeric payload=>\n', payload);
try {
if (!payload) {
throw Error(MESSAGE.BAD_REQUEST.NO_REQUIRED_PARAMETER({ payload }));
}
if (!_.isObject(payload)) {
throw Error(MESSAGE.BAD_REQUEST.PARAMETER_FORMAT_INVALID({ payload: typeof payload }));
}
const targetIndex = Object
.keys(payload)
.findIndex((e) => {
if (typeof payload[e] === 'string') {
return !Validator.isNumeric(payload[e], { no_symbols: true });
}
return !_.isFinite(payload[e]);
});
// console.log('targetIndex=>', targetIndex);
return (targetIndex === -1)
? null
: Object.keys(payload)[targetIndex];
// return this.checkInput(payload, {
// allowNull,
// allowUndefined,
// assertType: 'number',
// });
} catch (e) {
throw e;
}
},
/**
* 檢查輸入的 payload 是否全部都是字串
* @param {string|number} payload
* @returns {boolean} verification result
*/
isNotString(payload, {
allowNull = false,
allowUndefined = false,
} = {}) {
sails.log.debug('VerifyHelper isNotString payload=>\n', payload);
try {
return this.checkInput(payload, {
allowNull,
allowUndefined,
assertType: 'string',
});
} catch (e) {
throw e;
}
},
/**
* 驗證傳入的值是否為合格密碼。
* @param {string|number} payload
* @param {object} {
* isAlphabet = true,
* isAlphanumeric = false,
* locale = ['en-US'],
* minLength = 1,
* isNoSpace = true,
* }
* @returns {boolean} validation result
*/
isPassword(payload, {
isOnlyAlphabet = false,
isOnlyNumeric = false,
isAlphanumeric = true,
isNoSpace = true,
allowSymbols = null,
minLength = 1,
} = {}) {
sails.log.debug('VerifyHelper isPassword payload=>\n', payload);
let result = {
isValid: true,
rule: null,
};
try {
if (!payload) {
throw Error(MESSAGE.BAD_REQUEST.NO_REQUIRED_PARAMETER({ payload }));
}
// allowSymbols.reduce((f, r) => `/${f}/${r}`)
const vNumeric = () => Validator.isNumeric(payload, { no_symbols: true });
const vMinLength = () => Validator.isLength(payload, { min: minLength });
const vNoSpace = () => !Validator.isEmpty(payload);
// 檢查是否為純字母組合
if (isOnlyAlphabet && !vAllowSymbol(payload, ['a-z', 'A-Z'])) {
result = {
isValid: false,
rule: { ...result.rule, isOnlyAlphabet },
};
}
// 檢查是否為純數字組合
if (isOnlyNumeric && !vNumeric()) {
result = {
isValid: false,
rule: { ...result.rule, isOnlyNumeric },
};
}
// 檢查是否有數字字母組合
if (isAlphanumeric && !vAllowSymbol(payload, ['a-z', 'A-Z', '0-9'])) {
result = {
isValid: false,
rule: {
...result.rule,
isAlphanumeric,
},
};
}
// 檢查長度
if (minLength && !vMinLength()) {
result = {
isValid: false,
rule: { ...result.rule, minLength },
};
}
// 檢查空白字元
if (isNoSpace && !vNoSpace()) {
result = {
isValid: false,
rule: { ...result.rule, isNoSpace },
};
}
// 檢查特殊字元
if (allowSymbols) {
let symbols = allowSymbols;
if (isOnlyAlphabet) {
symbols = symbols.concat(['a-z', 'A-Z']);
}
if (isAlphanumeric) {
symbols = symbols.concat(['a-z', 'A-Z', '0-9']);
}
result = {
isValid: vAllowSymbol(payload, symbols),
rule: { ...result.rule, allowSymbols },
};
}
return result;
} catch (e) {
throw e;
}
},
validateJson({
schema = null,
data = null,
}, {
debug = false,
} = {}) {
try {
const Logger = (...message) => (debug
? console.debug(...message)
: null);
if (!_.isObject(schema)) {
throw Error(MESSAGE.BAD_REQUEST.PARAMETER_FORMAT_INVALID({ schema: typeof schema }));
}
if (!_.isObject(data)) {
throw Error(MESSAGE.BAD_REQUEST.PARAMETER_FORMAT_INVALID({ data: typeof data }));
}
// console.log('Object.keys(data)=>', Object.keys(data));
let validatorRule;
const index = Object
.keys(schema)
.findIndex((eachKey) => {
const eachSchema = schema[eachKey];
const eachItem = data[eachKey];
const isRequired = eachSchema.required;
Logger('\n\n===============\n');
Logger('validate key=>', eachKey, ', value=>', eachItem);
if (isRequired && _.isNil(eachItem)) {
Logger('_.isNil(eachItem)=>', _.isNil(eachItem));
validatorRule = ['isRequired'];
return true;
}
if (!_.isNil(eachItem)) {
const mode = eachSchema.mode || 'some';
return eachSchema.validator[mode]((rule) => {
Logger('------------');
Logger('rule=>', rule);
validatorRule = rule;
const validatorName = _.first(rule);
const validator = jsonValidator[validatorName];
Logger('validator=>', validatorName);
if (_.isNil(validator)) {
throw Error(`[!] target validator "${validatorName}" not exists!`);
}
const options = _.last(rule);
// const runnable = new Promise((resolve, reject) => {
return !validator(eachItem, options);
// .then(res => resolve(res))
// .catch(err => reject(err));
// });
// const result = runnable();
// Logger('res=>', !result);
// return !result;
});
}
return false;
});
Logger('index=>', index);
const isValid = (index === -1);
const errorKey = Object.keys(schema)[index];
return {
isValid,
errorKey: isValid
? null
: errorKey,
errorValue: isValid
? null
: data[errorKey],
errorRule: isValid
? null
: validatorRule,
};
} catch (err) {
throw err;
}
},
getJoi: () => Joi,
getJoiObjectSchema(keys) {
return _.isFunction(keys)
? Joi.object().keys(keys(Joi))
: Joi.object().keys(keys);
},
getJoiValidate({
value,
schema,
options,
callback,
}) {
return Joi.validate(
value,
_.isFunction(schema)
? (
_.isObject(options)
? Joi
.object()
.options(options)
.keys(schema(Joi))
: Joi
.object()
.keys(schema(Joi))
)
: schema,
options,
callback,
);
},
getJoiBasicListParamSchema() {
const j = this.getJoi();
return {
langCode: j.string().optional(),
curPage: j.number().integer().min(1).optional(),
perPage: j.number().integer().min(1).optional(),
sort: j.string().optional(),
sortBy: j.string().optional(),
keyword: j.string().optional().empty(''),
};
},
getJoiRequiredSchemaByKey({ schema, requiredKeys = [] }) {
const schemaWithRequiredKeys = Object.assign({}, schema);
if (requiredKeys && requiredKeys.length > 0) {
requiredKeys.forEach((e) => {
schemaWithRequiredKeys[e] = schema[e].required();
});
}
// console.log('scheme=>', schema);
return schemaWithRequiredKeys;
},
};
module.exports = {
...Validator,
...theValidator,
};