GillesRasigade/pattern

View on GitHub
src/Validator/Validator.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';

const tv4 = require('tv4');

/**
 * Validator a Javascript against a JSON Schema thanks to tv4 module.
 *
 * @class Validator
 *
 * @see https://github.com/geraintluff/tv4
 *
 */
class Validator {
  constructor() {
    // Attach a fresh tv4 API to this validator:
    this.tv4 = tv4.freshApi();
  }

  /**
   * Check if the object is valid against its JSON Schema definition.
   *
   * @param  {Object}  obj - Object with toJSON() implemented
   * @return {Boolean}     - True if valid, False otherwise
   */
  isValid(obj) {
    // Get the object schema:
    const schema = this.getSchema(obj);

    const json = typeof obj.toJSON === 'function' ? obj.toJSON() : obj;

    // Validate the object against its schema:
    const isValid = this.tv4.validate(json, schema);

    // Attach the validation error to the object if supported:
    // console.error(this.tv4.error);
    if (isValid !== true && typeof obj.setValidationError === 'function') {
      obj.setValidationError(this.tv4.error);
    }

    return isValid;
  }

  /**
   * Get the JSON Schema definition from the object given as input parameter.
   *
   * @param  {Object} obj - Object to search a JSON Schema for
   * @return {Object}     - JSON Schema definition
   */
  getSchema(obj) {
    // Retrieve the object constructor name:
    const constructor = obj.constructor.name;

    return this.tv4.getSchema(`${constructor}.json`)
      || this.addSchema(obj);
  }

  /**
   * Add a Schema definition for a specific object.
   *
   * @param  {Object} obj    - Object to attache to definition on
   * @param  {String} [path] - Path to the JSON Schema definition
   * @return {Object}        - JSON Schema definition
   */
  addSchema(obj, path) {
    // Retrieve the object constructor name:
    const constructor = this.getClassName(obj);

    let schema = null;
    if (typeof path === 'string') {
      schema = require(path); // eslint-disable-line global-require
    } else if (typeof path === 'object') {
      schema = path;
    } else if (obj.hasOwnProperty('schema')) {
      schema = obj.schema;
    } else if (typeof obj.getValidationSchema === 'function') {
      schema = obj.getValidationSchema();
    } else {
      schema = {};
    }

    // Add the object Schema:
    this.tv4.addSchema(`${constructor}.json`, schema);

    return schema;
  }

  /**
   * Get the className from an object.
   *
   * @param  {String|Object} obj - Object or string
   * @return {String}            - Name of the constructor
   */
  getClassName(obj) {
    // Get the constructor name:
    let name = null;
    if (typeof obj === 'string') {
      name = obj;
    } else if (obj.prototype && obj.prototype.constructor.name) {
      name = obj.prototype.constructor.name;
    } else {
      name = obj.constructor.name;
    }
    return name;
  }
}

module.exports = Validator;