ottoman
Version:
Ottoman Couchbase ODM
253 lines • 8.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeHooks = exports.addValidators = exports.registerType = exports.applyDefaultValue = exports.validate = exports.buildFields = void 0;
const utils_1 = require("../../utils");
const errors_1 = require("../errors");
const schema_1 = require("../schema");
const types_1 = require("../types");
const cast_strategy_1 = require("../../utils/cast-strategy");
/**
* Build the fields using the given definition.
* If the [[obj]] is a schema instance, the data will be taken from the fields provided.
* @function
* @public
*
* @param {Schema|Object} obj the definition or schema instance
* @param strict will drop all properties not defined in the schema
* @returns {FieldMap}
* @throws {Error}
*
* @example
* ```ts
* const fields = buildFields({ name: String, hasChild: { type: Boolean, default: true } });
* ```
*/
const buildFields = (obj, strict = true) => {
if (obj instanceof schema_1.Schema) {
return obj.fields;
}
const fields = {};
const keys = Object.keys(obj);
for (const _key of keys) {
const opts = obj[_key] !== undefined && obj[_key] !== null ? _parseType(obj[_key], strict) : { type: false };
if (!opts.type) {
throw new errors_1.BuildSchemaError(`Property '${_key}' is a required type`);
}
fields[_key] = _makeField(_key, opts, obj[_key]);
}
return fields;
};
exports.buildFields = buildFields;
/**
* Parse the definition of a field in the schema to identify its type.
* @function
* @private
* @param value that is going to parsed
* @param strict Schema Validation Strategy Strict
* @throws BuildSchemaError
*/
const _parseType = (value, strict = true) => {
if (value instanceof schema_1.Schema) {
return _makeParseResult('Embed', value);
}
else if ((0, utils_1.is)(value, Array)) {
return _makeParseResult(Array.name, _makeField('', _parseType(value[0], strict)));
}
else if (value instanceof types_1.CoreType || (0, utils_1.isSchemaFactoryType)(value, schema_1.Schema.FactoryTypes)) {
return _makeParseResult(_getFieldType(value), {});
}
else if ((0, utils_1.is)(value, Object)) {
if (Object.keys(value).length === 0) {
return _makeParseResult('Mixed', value);
}
else if (value.ref) {
const options = {
schema: new schema_1.Schema(value.type, { strict }),
refModel: value.ref,
};
return _makeParseResult('Reference', options);
}
else if (typeof value.type !== 'undefined') {
if ((0, utils_1.is)(value.type, Array)) {
return _makeParseResult(Array.name, _makeField('', _parseType(value.type[0], strict)));
}
return Object.assign(Object.assign({}, _parseType(value.type, strict)), { options: value });
}
else {
return _makeParseResult('Embed', new schema_1.Schema(value, { strict }));
}
}
else {
return _makeParseResult('Unknown', value);
}
};
/**
* Create the structure of schema object.
* @private
* @param type of the field
* @param options of the schema
* */
const _makeParseResult = (type, options) => {
return { type, options };
};
/**
* Get the String type of a field.
* @private
* @param type of the field
* */
const _getFieldType = (type) => {
return type ? type.name || type.constructor['sName'] || undefined : undefined;
};
/**
* Make a field using its definition, throw a [[BuildSchemaError]] if the type is not supported.
* @private
* @param name of the field
* @param def result of parsing the field schema
* @throws BuildSchemaError
*/
const _makeField = (name, def, arrayOptions) => {
const typeFactory = schema_1.Schema.FactoryTypes[String(def.type)];
if (typeFactory === undefined) {
throw new errors_1.BuildSchemaError(`Unsupported type specified in the property '${name}'`);
}
if (arrayOptions !== undefined) {
return typeFactory(name, def.options, arrayOptions);
}
return typeFactory(name, def.options);
};
/**
* Validate data using the schema definition.
* @param data that is going to be validated
* @param schema that will be used to validate
* @param options
* @throws BuildSchemaError, ValidationError
* @example
* ```ts
* const data = {
* name: "John",
* age: "50"
* };
* const schema = new Schema({
* name: String,
* age: {type: Number, intVal: true}
* });
* const strictSchema = new Schema({
* name: String,
* age: {type: Number, intVal: true}
* },
* {
* validationStrategy: VALIDATION_STRATEGY.STRICT
* });
* console.log(castSchema(data, schema)); // Print { name: "John", age: 50 }
* console.log(castSchema(data, strictSchema)); // Throw "Property age must be of type Number"
* ```
*/
const validate = (data, schema, options = {
strategy: cast_strategy_1.CAST_STRATEGY.THROW,
strict: true,
skip: [],
}) => {
const _schema = schema instanceof schema_1.Schema ? schema : new schema_1.Schema(schema);
const _data = (0, cast_strategy_1.cast)(data, _schema, options);
const skip = options.skip || [];
const errors = [];
for (const key in _schema.fields) {
const type = _schema.fields[key];
if (!skip.includes(type.name)) {
try {
const value = _data[type.name];
type.validate(value, options.strict);
}
catch (e) {
errors.push(e.message);
}
}
}
if (errors.length > 0) {
throw new errors_1.ValidationError(errors.join(', '));
}
return _data;
};
exports.validate = validate;
/**
* Apply default values defined on schema to an object instance.
* @param obj reference to object instance
* @param schema definition will be used to determine default definitions
*
* @example
* ```ts
* const schema = { name: { type: String, default: 'John' }, hasChild: { type: Boolean, default: true } };
* const obj: any = applyDefaultValue(obj, schema)
*
* console.log(obj);
* ```
*/
const applyDefaultValue = (obj, schema) => {
const _schema = schema instanceof schema_1.Schema ? schema : new schema_1.Schema(schema);
return _schema.applyDefaultsToObject(obj);
};
exports.applyDefaultValue = applyDefaultValue;
/**
* Register a custom type to Schema supported types.
* @function
* @param name
* @param factory
* @throws Error
* @example
* ```ts
* registerType(Int8.name, (fieldName, opts) => new Int8(fieldName, opts.required));
* ```
*/
const registerType = (name, factory) => {
if (schema_1.Schema.FactoryTypes[name] !== undefined) {
throw new errors_1.ValidationError(`A type with name '${name}' has already been registered`);
}
schema_1.Schema.FactoryTypes[name] = factory;
};
exports.registerType = registerType;
/**
* Register custom validators to Schema validators register.
* @function
* @param validators
* @throws Error
* @example
* ```ts
* addValidators({
* email: (value) => {
* regexp = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
* if (!regexp.test(value)) {
* throw new Error('Email address is invalid.')
* }
* }
* });
*
* const ContactSchema = new Schema({
* name: String,
* contact: { type: String, validator: 'email' }
* });
* ```
*/
const addValidators = (validators) => {
if (!(0, utils_1.is)(validators, Object)) {
throw new errors_1.BuildSchemaError('Validators must be an object.');
}
for (const prop in validators) {
const validator = validators[prop];
if (typeof validator !== 'function') {
throw new errors_1.BuildSchemaError('Validator object properties must be functions.');
}
schema_1.Schema.validators[prop] = validator;
}
};
exports.addValidators = addValidators;
const mergeHooks = (hook, other) => {
const _hook = Object.assign({}, hook);
Object.keys(_hook).forEach((value) => {
if (other.hasOwnProperty(value)) {
_hook[value] = [..._hook[value], ...other[value]];
}
});
return _hook;
};
exports.mergeHooks = mergeHooks;
//# sourceMappingURL=fn-schema.js.map