"use strict" ;
const errorChain = require ( "error-chain" ) ;
let ValidationError = errorChain ( "ValidationError" ) ;
function isPresent ( value ) {
if ( value == null ) {
throw new ValidationError ( "Required value is missing" , { errorType : "isPresent" } ) ;
}
}
function isString ( value ) {
if ( value != null && typeof value !== "string" ) {
throw new ValidationError ( "Value is not a string" , { errorType : "isString" } ) ;
}
}
function isBoolean ( value ) {
if ( value != null && typeof value !== "boolean" ) {
throw new ValidationError ( "Value is not a boolean" , { errorType : "isBoolean" } ) ;
}
}
function isOneOf ( ... acceptableValues ) {
let valueSet = new Set ( acceptableValues ) ;
return function ( value ) {
if ( value != null && ! valueSet . has ( value ) ) {
throw new ValidationError ( "Value is not one of the specified acceptable values" , {
errorType : "isOneOf" ,
acceptableValues : acceptableValues
} ) ;
}
} ;
}
/* NOTE: Extremely sketchy way of tracking validation state via a module-global object, but we can get away with this because validation is completely synchronous */
let context ;
function assertProperties ( object , properties ) {
for ( let [ property , assertions ] of Object . entries ( properties ) ) {
context . path . push ( property ) ;
let value = object [ property ] ;
try {
for ( let assertion of assertions ) {
assertion ( value ) ;
}
} catch ( error ) {
if ( error . input == null ) {
error . input = value ;
}
throw error ;
}
context . path . pop ( ) ;
}
}
/* FIXME: Abstract out the path and value assignment for errors into a `withPath(...)` abstraction */
function arrayOf ( assertions ) {
return function ( value ) {
if ( value != null ) {
if ( ! Array . isArray ( value ) ) {
throw new ValidationError ( "Value is not an array" , { errorType : "arrayOf" } ) ;
} else {
for ( let [ i , item ] of value . entries ( ) ) {
context . path . push ( i ) ;
try {
for ( let assertion of assertions ) {
assertion ( item ) ;
}
} catch ( error ) {
if ( error . input == null ) {
error . input = value ;
}
throw error ;
}
context . path . pop ( ) ;
}
}
}
} ;
}
function validate ( validator , value ) {
context = {
path : [ ]
} ;
try {
validator ( value ) ;
} catch ( error ) {
if ( error . path == null ) {
error . path = context . path ;
}
throw error ;
}
context = undefined ;
}
module . exports = {
validate , assertProperties , ValidationError ,
isPresent , isString , isOneOf , arrayOf , isBoolean
} ;
// try {
// validate(validateLoginPayload, {
// type: 'm.login.password',
// password: 'test',
// identifier: { type: 'm.id.user', user: 'joepie91' },
// initial_device_display_name: 'http://localhost:3001/index.html via Firefox on Linux',
// user: 'joepie91',
// arrayStuff: [ 42 ]
// });
// } catch (error) {
// console.log(require("util").inspect(error, {colors: true, depth: null}));
// }