Add server/src/validator
This commit is contained in:
parent
715c40f60f
commit
6e94b68598
4
packages/server/src/validator/IValidationMessage.ts
Normal file
4
packages/server/src/validator/IValidationMessage.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IValidationMessage {
|
||||
readonly property: string | number | symbol
|
||||
readonly message: string
|
||||
}
|
||||
17
packages/server/src/validator/ValidationError.ts
Normal file
17
packages/server/src/validator/ValidationError.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {IValidationMessage} from './IValidationMessage'
|
||||
|
||||
export class ValidationError extends Error {
|
||||
readonly name: string
|
||||
constructor(
|
||||
readonly errors: IValidationMessage[],
|
||||
message?: string,
|
||||
) {
|
||||
super(
|
||||
message
|
||||
? message
|
||||
: 'Validation failed on properties: ' +
|
||||
errors.map(e => e.property).join(', '))
|
||||
this.name = 'ValidationError'
|
||||
Error.captureStackTrace(this)
|
||||
}
|
||||
}
|
||||
84
packages/server/src/validator/Validator.test.ts
Normal file
84
packages/server/src/validator/Validator.test.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import {Validator} from './Validator'
|
||||
|
||||
describe('Validator', () => {
|
||||
|
||||
const entity = {
|
||||
a: 0,
|
||||
b: 1,
|
||||
c: 2,
|
||||
d: undefined,
|
||||
e: false,
|
||||
f: null,
|
||||
}
|
||||
|
||||
let v!: Validator<typeof entity>
|
||||
beforeEach(() => {
|
||||
v = new Validator(entity)
|
||||
})
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws when an entity is not provided', () => {
|
||||
expect(() => new Validator(undefined))
|
||||
.toThrowError(/record could not be found/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ensure', () => {
|
||||
it('adds an error when a value is falsy', () => {
|
||||
v.ensure('a')
|
||||
expect(v.errors.length).toBe(1)
|
||||
v.ensure('e')
|
||||
expect(v.errors.length).toBe(2)
|
||||
})
|
||||
it('adds an error when a value does not exist', () => {
|
||||
v.ensure('b')
|
||||
expect(v.errors.length).toBe(0)
|
||||
v.ensure('b')
|
||||
expect(v.errors.length).toBe(0)
|
||||
v.ensure('c')
|
||||
expect(v.errors.length).toBe(0)
|
||||
v.ensure('d')
|
||||
expect(v.errors.length).toBe(1)
|
||||
})
|
||||
|
||||
it('adds an error when a value does not match', () => {
|
||||
v.ensure('a', entity.a)
|
||||
v.ensure('b', entity.b)
|
||||
v.ensure('c', entity.c)
|
||||
v.ensure('d', entity.d)
|
||||
v.ensure('e', entity.e)
|
||||
v.ensure('f', entity.f)
|
||||
expect(v.errors.length).toBe(0)
|
||||
|
||||
v.ensure('f', undefined)
|
||||
expect(v.errors.length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getError', () => {
|
||||
it('returns undefined when no error', () => {
|
||||
v.ensure('a', entity.a)
|
||||
expect(v.getError()).toBe(undefined)
|
||||
})
|
||||
it('returns an error when a validation errors was encountered', () => {
|
||||
v.ensure('a', 999)
|
||||
const err = v.getError()!
|
||||
expect(err).toBeTruthy()
|
||||
expect(err.name).toEqual('ValidationError')
|
||||
expect(err.message).toMatch(/Validation failed on properties/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('throw', () => {
|
||||
it('does nothing when no validation errors', () => {
|
||||
v.ensure('a', entity.a)
|
||||
v.throw()
|
||||
})
|
||||
|
||||
it('throws when there are validation errors', () => {
|
||||
v.ensure('a', 999)
|
||||
expect(() => v.throw()).toThrowError()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
48
packages/server/src/validator/Validator.ts
Normal file
48
packages/server/src/validator/Validator.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import {IValidationMessage} from './IValidationMessage'
|
||||
import {ValidationError} from './ValidationError'
|
||||
|
||||
export class Validator<T> {
|
||||
|
||||
readonly errors: IValidationMessage[] = []
|
||||
readonly entity: T
|
||||
|
||||
constructor(entity: T | undefined) {
|
||||
if (!entity) {
|
||||
throw new ValidationError([], 'The record could not be found')
|
||||
}
|
||||
this.entity = entity
|
||||
}
|
||||
|
||||
ensure(property: keyof T, value?: any): this {
|
||||
if (arguments.length === 1) {
|
||||
if (!this.entity[property]) {
|
||||
this.addError(property, `The property "${property}" is invalid`)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
if (this.entity[property] !== value) {
|
||||
this.addError(property,
|
||||
`The property "${property}" should be equal to "${value}"` +
|
||||
` but the actual value is "${this.entity[property]}"`)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
getError(): ValidationError | undefined {
|
||||
if (this.errors.length) {
|
||||
return new ValidationError(this.errors)
|
||||
}
|
||||
}
|
||||
|
||||
throw() {
|
||||
const error = this.getError()
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
protected addError(property: keyof T, message: string) {
|
||||
this.errors.push({property, message})
|
||||
}
|
||||
}
|
||||
3
packages/server/src/validator/index.ts
Normal file
3
packages/server/src/validator/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './IValidationMessage'
|
||||
export * from './ValidationError'
|
||||
export * from './Validator'
|
||||
Loading…
x
Reference in New Issue
Block a user