blow-data
Version:
Data access layer for Blow.
83 lines (76 loc) • 2.46 kB
text/typescript
;
import * as Joi from 'joi';
import {isFunction} from 'util';
import {Observable} from 'rxjs';
import {ValidationResult} from './ValidationResult';
import {IValidator, IValidationResult} from './interfaces';
export class Validator implements IValidator {
protected _syncSchema;
protected _asyncSchema;
constructor(syncSchema, asyncSchema?) {
this._syncSchema = syncSchema;
this._asyncSchema = asyncSchema;
}
protected _syncValidate(data) {
if(!this._syncSchema) {
return Observable.of([]);
}
const validationOptions = {
abortEarly: false,
allowUnknown: true
};
const result = Joi.validate(data, this._syncSchema, validationOptions);
if(!result.error) {
return Observable.of([]);
}
return Observable.of(result.error.details.map(r => {
r['property'] = r['path'];
const type = r['type'].split('.');
if(type.length > 1) {
type.shift();
}
r['type'] = type.join('.');
delete r['context'];
delete r['path'];
return r;
}));
}
protected _asyncValidate(data) {
if(!this._asyncSchema) {
return Observable.of([]);
}
return Observable.from(Object.keys(this._asyncSchema))
.mergeMap(propertyName => {
return Observable.from(this._asyncSchema[<string>propertyName])
.mergeMap(validator => {
const result = (<Function>validator).bind(data)(propertyName);
if(isFunction(result.subscribe)) {
return result;
}
return Observable.of(result);
})
.map(errorMessage => {
if(errorMessage) {
return {
property: propertyName,
type: 'custom',
message: errorMessage
};
}
return null;
});
})
.toArray()
.map(result => result.reduce((aggr, curr) => {
if(curr) {
(<[]>aggr).push(curr);
}
return aggr;
}, []));
}
validate(data): Observable<IValidationResult> {
const mapResults = (sync, async) => sync.concat(async);
return Observable.combineLatest(this._syncValidate(data), this._asyncValidate(data), mapResults)
.map(resultRaw => new ValidationResult(resultRaw));
}
}