r2js/r2inspector

View on GitHub
index.js

Summary

Maintainability
B
5 hrs
Test Coverage
const is = require('is_js');
const traverse = require('traverse');
const dot = require('dotty');
const _ = require('underscore');

const getOptional = (options, obj) => {
  let optional;
  if (is.boolean(options.required)) {
    optional = !options.required;
  } else if (is.boolean(options.optional)) {
    optional = options.optional;
  } else {
    optional = true;
  }

  Object.assign(obj, { optional });
  if (!optional) {
    Object.assign(obj, { $notEmpty: true });
  }
};

const getObjectId = (type, options, obj) => {
  if (type === 'objectid') {
    Object.assign(obj, {
      type: 'string',
      $objectId: true,
      ref: options.ref,
    });
  }
};

const getAllowHtml = (type, field, options, obj) => {
  if (type === 'string' &&
      !options.allowHtml &&
      !field.enumValidator) {
    Object.assign(obj, { $cleanHtml: true });
  }
};

const getPattern = (type, options, obj) => {
  const match = options.match;
  const pattern = options.pattern;

  if (match) {
    try {
      new RegExp(match); // eslint-disable-line
      Object.assign(obj, { pattern: match });
    } catch (e) {
      Object.assign(options, { match: undefined });
    }
  } else if (pattern && type === 'string') {
    // pattern string alanlarda kullanılabiliyor
    Object.assign(obj, { pattern });
  }
};

const getArrOpts = (options, props) => {
  if (options.arrOpts) {
    Object.assign(props, options.arrOpts);
  }

  if (!options.arrOpts || typeof options.arrOpts.optional === 'undefined') {
    Object.assign(props, { optional: true });
  }
};

const getValidators = (type, options, obj) => {
  const {
    minLength, maxLength, exactLength, min, max, lt, lte, gt, gte, ne,
  } = options;

  if (type === 'string' || type === 'array') {
    Object.assign(obj, _.pick({
      minLength, maxLength, exactLength,
    }, _.identity));
  }

  // number properties
  if (type === 'number') {
    Object.assign(obj, _.pick({
      min, max, lt, lte, gt, gte, ne,
    }, _.identity));
  }

  // equal
  if (['array', 'string', 'number', 'boolean'].includes(type)) {
    if (options.enum) {
      Object.assign(obj, { eq: options.enum });
    } else if (options.eq) {
      Object.assign(obj, { eq: options.eq });
    }
  }

  // default value
  if (typeof options.default !== 'undefined') {
    Object.assign(obj, { def: options.default });
  }

  // settings
  if (typeof options.settings !== 'undefined') {
    Object.assign(obj, { settings: options.settings });
  }
};

const process = (paths, obj) => {
  _.each(paths, (field, name) => {
    if (name.startsWith('_')) {
      return false;
    }

    // array of objects
    if (field.schema) {
      const arrOpts = {
        type: 'array',
        items: {
          type: 'object',
          strict: true,
          properties: {},
        },
      };

      getArrOpts(field.options, arrOpts);
      Object.assign(obj, { [name]: arrOpts });
      return process(
        field.schema.paths,
        obj[name].items.properties);
    }

    const type = field.instance.toLowerCase();
    const props = { type };

    let arrType;
    let options;
    if (type === 'array') {
      arrType = field.caster.instance.toLowerCase();
      props.type = 'array';
      props.items = { type: arrType };
      options = Object.assign(field.caster.options, field.options);
      getArrOpts(options, props);
    }

    const getType = arrType || type;
    const getOpts = options || field.options;
    const getProps = props.items || props;
    getOptional(getOpts, getProps);
    getObjectId(getType, getOpts, getProps);
    getAllowHtml(getType, field, getOpts, getProps);
    getPattern(getType, getOpts, getProps);
    getValidators(getType, getOpts, getProps);

    if (name.includes('.')) {
      const nameArr = name.split('.');
      if (!obj[nameArr[0]]) {
        dot.put(obj, nameArr[0], {
          type: 'object',
          strict: true,
          optional: true,
          properties: {},
        });
      }
      dot.put(obj, `${nameArr[0]}.properties.${nameArr[1]}`, getProps);
    }

    return Object.assign(obj, { [name]: props });
  });
};

module.exports = function Inspector() {
  return (mongoose, modelName) => {
    const model = mongoose.model(modelName);

    if (!model.inspector) {
      const obj = {
        type: 'object',
        strict: true,
        optional: false,
        properties: {},
      };

      process(model.schema.paths, obj.properties);
      model.inspector = obj;

      const updateObj = JSON.parse(JSON.stringify(obj));
      traverse(updateObj).forEach(function (x) { // eslint-disable-line
        if (typeof x.optional !== 'undefined') {
          this.update(Object.assign(x, { optional: true }));
        }
      });

      model.inspectorUpdate = updateObj;
    }
  };
};