Advertisement
Guest User

Chainable Express Validation

a guest
Dec 3rd, 2016
308
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. class Validation {
  2.   constructor(data) {
  3.     this._data = data;
  4.     this._valid = true;
  5.     this._skip = false; // Flag to skip validation until next .expect() or .go()
  6.     this._errors = []; // Store errors here
  7.     this._keys = []; // Store invalid keys here
  8.   }
  9.  
  10.   // Store checked value in this._value for chained checking
  11.   _get() {
  12.     if (typeof this._data[this._key] !== 'undefined') return this._data[this._key];
  13.     let parts = this._key.split('.'),
  14.       value = this._data;
  15.     for (let i = 0; i < parts.length; i++) {
  16.       try {
  17.         value = value[parts[i]];
  18.       } catch(e) {
  19.         return void 0;
  20.       }
  21.       if (typeof value === 'undefined') return void 0;
  22.     }
  23.     return value;
  24.   }
  25.  
  26.   // Push error object into this._errors
  27.   _add(message) {
  28.     this._errors.push({ path: this._key, message: message });
  29.     this._keys.push(this._key);
  30.     if (this._valid) this._valid = false;
  31.     return this;
  32.   }
  33.  
  34.   // Helper to find out if a particular key has already been invalidated
  35.   _already() {
  36.     return this._keys.indexOf(this._key) !== -1;
  37.   }
  38.  
  39.   // Start validation chain for a key, store key in this._key
  40.   expect(key) {
  41.     if ([ 'string', 'number', 'boolean' ].indexOf(typeof key) === -1)
  42.       throw new Error('Validation key in .expect() must be a either a string, a number, or a boolean');
  43.     if (this._already()) {
  44.       this._skip = true;
  45.       return this;
  46.     }
  47.     this._skip = false;
  48.     this._optional = false;
  49.     this._nullable = false;
  50.     this._key = key;
  51.     this._value = this._get();
  52.     return this;
  53.   }
  54.  
  55.   present(message) {
  56.     if (this._skip) return this;
  57.     if (this._already()) return this;
  58.     if (typeof this._key === 'undefined') throw new Error('Must call .expect() before .present()');
  59.     if (this._optional) throw new Error('Cannot call .present() after .optional()');
  60.     if (typeof message === 'undefined') message = 'This is a required parameter';
  61.     if (typeof this._value === 'undefined') this._add(message);
  62.     return this;
  63.   }
  64.  
  65.   string(message) {
  66.     if (this._skip) return this;
  67.     if (this._already()) return this;
  68.     if (typeof this._key === 'undefined') throw new Error('Must call .expect() before .string()');
  69.     if (this._optional && typeof this._value === 'undefined') return this;
  70.     if (this._nullable && this._value === null) return this;
  71.     if (typeof message === 'undefined') message = 'The parameter must be a string';
  72.     if (typeof this._value !== 'string') this._add(message);
  73.     return this;
  74.   }
  75.  
  76.   go(handler) {
  77.     if (typeof handler !== 'function') throw new Error('Handler in .go() must be a function');
  78.     if (!this._valid) {
  79.       let error = new Error('Validation Error');
  80.       error.name = 'ValidationError';
  81.       error.errors = this._errors;
  82.       return process.nextTick(handler, error);
  83.     }
  84.     process.nextTick(handler);
  85.   }
  86. }
  87.  
  88. const validate = data => new Validation(data);
  89.  
  90. validate({ name: { last: 'foo' }})
  91.     .expect('name.first').present().string() // Duplicate this line ~2000 times for error
  92.     .go(console.log);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement