Home Identifier Source Repository

lib/Validation.js

/**
 * Created by AlexanderC on 6/15/15.
 */

'use strict';

import Kernel from 'deep-kernel';
import {ObjectToJoi} from './ObjectToJoi';
import {ModelNotFoundException} from './Exception/ModelNotFoundException';
import Joi from 'joi';
import {ObjectValidationFailedException} from './Exception/ObjectValidationFailedException';
import {Exception} from './Exception/Exception';

/**
 * Validation engine
 */
export class Validation extends Kernel.ContainerAware {
  /**
   * @param {Array} models
   */
  constructor(models = []) {
    super();

    this._immutable = false;
    this._models = this._rawModelsToSchemas(models);
  }

  /**
   * @param {Boolean} state
   */
  set immutable(state) {
    this._assureImmutable();

    this._immutable = state;
  }

  /**
   * @returns {Boolean}
   */
  get immutable() {
    return this._immutable;
  }

  /**
   * @param {String} modelName
   * @param {*} valueObject
   * @returns {*}
   */
  validate(modelName, valueObject) {
    let model = this.get(modelName);
    let result = Joi.validate(valueObject, Joi.object().keys(model));

    if (result.error) {
      throw new ObjectValidationFailedException(modelName, result.error);
    }

    return result.value;
  }

  /**
   * @returns {Object[]}
   */
  get models() {
    return this._models;
  }

  /**
   * @param {String} modelName
   * @param {Object} schema
   * @returns {Validation}
   */
  setRaw(modelName, schema) {
    this._assureImmutable();

    this._models[modelName] = Validation.normalizeSchema(schema);

    return this;
  }

  /**
   * @param {String} modelName
   * @param {Object} schema
   * @returns {Validation}
   */
  set(modelName, schema) {
    this._assureImmutable();

    this._models[modelName] = schema;

    return this;
  }

  /**
   * @param {String} modelName
   * @returns {Boolean}
   */
  has(modelName) {
    return typeof this._models[modelName] !== 'undefined';
  }

  /**
   * @param {String} modelName
   * @returns {Object}
   */
  get(modelName) {
    if (!this.has(modelName)) {
      throw new ModelNotFoundException(modelName);
    }

    return this._models[modelName];
  }

  /**
   * Booting a certain service
   *
   * @param {Kernel} kernel
   * @param {Function} callback
   */
  boot(kernel, callback) {
    this._assureImmutable();

    this._models = this._rawModelsToSchemas(kernel.config.models);

    callback();
  }

  /**
   * @param {Array} rawModels
   * @returns {Object}
   * @private
   */
  _rawModelsToSchemas(rawModels) {
    let modelsSchema = {};

    for (let modelKey in rawModels) {
      if (!rawModels.hasOwnProperty(modelKey)) {
        continue;
      }

      let backendModels = rawModels[modelKey];

      for (let modelName in backendModels) {
        if (!backendModels.hasOwnProperty(modelName)) {
          continue;
        }

        let schema = backendModels[modelName];

        // @todo: move this sh*t into DB somehow...
        schema.Id = schema.Id || 'timeUUID';

        modelsSchema[modelName] = Validation.normalizeSchema(schema);
      }
    }

    return modelsSchema;
  }

  /**
   * @private
   */
  _assureImmutable() {
    if (this._immutable) {
      throw new Exception('You are not abble to alter the state after setting it immutable');
    }
  }

  /**
   * @param {Object} rawSchema
   * @returns {Object}
   */
  static normalizeSchema(rawSchema) {
    return new ObjectToJoi(rawSchema).transform();
  }
}