moleculer
moleculer copied to clipboard
Error: Invalid 'undefined' type in validator schema.
Prerequisites
Please answer the following questions for yourself before submitting an issue.
- [x ] I am running the latest version
- [ x] I checked the documentation and found no answer
- [x ] I checked to make sure that this issue has not already been filed
- [x ] I'm reporting the issue to the correct repository
Current Behavior
Running moleculer in "development mode" using a custom validator "Joi" after saving the file "package.json" (without any changes), Runner detect a "change", a soon moleculer start it will throw an error.
**[Runner] Invalid 'undefined' type in validator schema. Error: Invalid 'undefined' type in validator schema.**
Here is the custom validator using Joi
const BaseValidator = require('moleculer').Validators.Base;
const { ValidationError } = require('moleculer').Errors;
const _ = require('lodash');
class JoiValidator extends BaseValidator {
constructor() {
super();
this.Joi = require('joi');
}
compile(schema) {
//Check if schema is joi format
if (this.Joi.isSchema(schema)) {
return (params) => this.validate(params, schema);
} else if (this.validator) {
//Use fastest-validator
return this.validator.compile(_.cloneDeep(schema));
}
}
/**
* Clean messages
* From "\"name\" is required"
* to "name is required"
* @param {string} msg
* @returns {string}
*/
cleanMessage(msg) {
if (!msg) return '';
return _.replace(msg, /([^\w\d\s])+/g, '');
}
/**
* Validate joi schema
* @param {Object} params
* @param {*} schema
* @returns
*/
validate(params, schema) {
let errors = [];
if (!this.Joi.isSchema(schema) && _.isFunction(schema)) {
throw new ValidationError('Not Joi Schema', null, {
message: 'No Schema Joi',
});
}
// Check if is merged schema
if (_.get(params, 'body', null)) {
params = params.body;
}
//Validate schema
const { error } = schema.validate(params, {
abortEarly: true,
});
if (!_.isEmpty(error)) {
/**
* Create Custom Error
*/
if (error.details) {
_.forEach(error.details, (value) => {
let errorObj = {
type: null,
field: null,
uid: null,
message: null,
};
//Assign type
if (value.type) {
errorObj.type = value.type;
}
//Assign field name
if (value.context) {
errorObj.field = value.context.key;
}
//Assign message error
if (value.message) {
errorObj.message = this.cleanMessage(value.message);
}
//Create uid
if (errorObj.type && errorObj.field) {
errorObj.uid = `${errorObj.field}.${errorObj.type}`;
}
errors.push(errorObj);
});
}
/**
* Return errors
*/
if (error) {
throw new ValidationError(
this.cleanMessage(error.message),
null,
errors
);
}
}
return true;
}
}
module.exports = JoiValidator;
Steps to Reproduce
Please provide detailed steps for reproducing the issue.
- Create a custom validator (using the above code)
- Create an example service and add validations using Joi Scheme
Joi.object({
name: Joi.string().required(),
})
- Run Moleculer (should run well)
- Open "package.json" and don't do anything just save the file, after that runner will throw an error
Context
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
- Moleculer version: 0.14.23
- NodeJS version: 16 LTS
- Operating System: Windows 11 /Ubuntu 20.4 LTS
I believe this happens because on https://github.com/moleculerjs/moleculer/blob/master/src/validators/index.js#L32 this function is trying to look for a match on the object "Validators" but it doesn't exist so it runs a default as "Fastest" and when it reaches "Fastest" compile function, it won't work as Joi schema is an object with functions, (no valid schema for Fastest)
So I'm not sure if the middleware (resolve function) should search for the "custom" validator on the moleculer.config.js
I try to create a small solution that is updating this function resolve
like this
function resolve(opt) {
if (opt instanceof Validators.Base) {
return opt;
} else if (isString(opt)) {
let ValidatorClass = getByName(opt);
if (ValidatorClass) return new ValidatorClass();
throw new BrokerOptionsError(`Invalid Validator type '${opt}'.`, { type: opt });
//If is a customer validator <======= this part
} else if(opt.constructor && opt.constructor.name) {
return opt;
} else if (isObject(opt)) {
let ValidatorClass = getByName(opt.type || "Fastest");
if (ValidatorClass) return new ValidatorClass(opt.options);
throw new BrokerOptionsError(`Invalid Validator type '${opt.type}'.`, {
type: opt.type
});
}
return new Validators.Fastest();
}
checking if the custom validator is a "class" looking for a constructor and the name, going this way, there's not an error at all, if no custom validator is found, it will continue to detect if is an "object" and check for the "type" or return a default validator "fastest"
Thanks the report, I've fixed it. Next version will be released.