@beecode/msh-util 1.2.1 → 2.0.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{lib → dist}/array-util.d.ts.map +1 -1
- package/dist/array-util.js +27 -0
- package/{lib → dist}/class-factory-pattern.d.ts.map +1 -1
- package/dist/class-factory-pattern.js +26 -0
- package/{lib → dist}/express/error-handler.d.ts.map +1 -1
- package/dist/express/error-handler.js +26 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/{lib → dist}/joi-util.d.ts.map +1 -1
- package/dist/joi-util.js +62 -0
- package/{lib → dist}/memoize-factory.d.ts +1 -1
- package/dist/memoize-factory.d.ts.map +1 -0
- package/dist/memoize-factory.js +24 -0
- package/{lib → dist}/object-util.d.ts.map +1 -1
- package/dist/object-util.js +115 -0
- package/dist/regex-util.js +12 -0
- package/{lib → dist}/single-threshold-promise.d.ts +1 -1
- package/dist/single-threshold-promise.d.ts.map +1 -0
- package/dist/single-threshold-promise.js +46 -0
- package/{lib → dist}/singleton/async.d.ts +1 -1
- package/dist/singleton/async.d.ts.map +1 -0
- package/dist/singleton/async.js +75 -0
- package/{lib → dist}/singleton/pattern.d.ts +1 -1
- package/dist/singleton/pattern.d.ts.map +1 -0
- package/dist/singleton/pattern.js +41 -0
- package/dist/string-util.js +18 -0
- package/dist/time-util.js +90 -0
- package/dist/time-zone.d.ts +467 -0
- package/dist/time-zone.d.ts.map +1 -0
- package/dist/time-zone.js +468 -0
- package/dist/timeout.js +17 -0
- package/dist/type-util.js +54 -0
- package/package.json +188 -134
- package/lib/array-util.js +0 -30
- package/lib/array-util.js.map +0 -1
- package/lib/class-factory-pattern.js +0 -30
- package/lib/class-factory-pattern.js.map +0 -1
- package/lib/express/error-handler.js +0 -28
- package/lib/express/error-handler.js.map +0 -1
- package/lib/index.d.ts +0 -14
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -30
- package/lib/index.js.map +0 -1
- package/lib/joi-util.js +0 -63
- package/lib/joi-util.js.map +0 -1
- package/lib/memoize-factory.d.ts.map +0 -1
- package/lib/memoize-factory.js +0 -28
- package/lib/memoize-factory.js.map +0 -1
- package/lib/object-util.js +0 -118
- package/lib/object-util.js.map +0 -1
- package/lib/regex-util.js +0 -15
- package/lib/regex-util.js.map +0 -1
- package/lib/single-threshold-promise.d.ts.map +0 -1
- package/lib/single-threshold-promise.js +0 -49
- package/lib/single-threshold-promise.js.map +0 -1
- package/lib/singleton/async.d.ts.map +0 -1
- package/lib/singleton/async.js +0 -78
- package/lib/singleton/async.js.map +0 -1
- package/lib/singleton/pattern.d.ts.map +0 -1
- package/lib/singleton/pattern.js +0 -45
- package/lib/singleton/pattern.js.map +0 -1
- package/lib/string-util.js +0 -21
- package/lib/string-util.js.map +0 -1
- package/lib/time-util.js +0 -97
- package/lib/time-util.js.map +0 -1
- package/lib/timeout.js +0 -21
- package/lib/timeout.js.map +0 -1
- package/lib/type-util.js +0 -57
- package/lib/type-util.js.map +0 -1
- package/lib/types/any-function/index.d.ts +0 -2
- package/lib/types/any-function/index.d.ts.map +0 -1
- package/lib/types/any-function/index.js +0 -3
- package/lib/types/any-function/index.js.map +0 -1
- package/lib/types/any-function/no-params.d.ts +0 -2
- package/lib/types/any-function/no-params.d.ts.map +0 -1
- package/lib/types/any-function/no-params.js +0 -3
- package/lib/types/any-function/no-params.js.map +0 -1
- package/lib/types/any-function/promise-no-params.d.ts +0 -2
- package/lib/types/any-function/promise-no-params.d.ts.map +0 -1
- package/lib/types/any-function/promise-no-params.js +0 -3
- package/lib/types/any-function/promise-no-params.js.map +0 -1
- package/lib/types/any-function/promise.d.ts +0 -2
- package/lib/types/any-function/promise.d.ts.map +0 -1
- package/lib/types/any-function/promise.js +0 -3
- package/lib/types/any-function/promise.js.map +0 -1
- package/src/array-util.test.ts +0 -50
- package/src/array-util.ts +0 -26
- package/src/class-factory-pattern.test.ts +0 -39
- package/src/class-factory-pattern.ts +0 -29
- package/src/express/error-handler.test.ts +0 -44
- package/src/express/error-handler.ts +0 -25
- package/src/index.ts +0 -25
- package/src/joi-util.test.ts +0 -192
- package/src/joi-util.ts +0 -65
- package/src/memoize-factory.test.ts +0 -40
- package/src/memoize-factory.ts +0 -27
- package/src/object-util.test.ts +0 -360
- package/src/object-util.ts +0 -127
- package/src/regex-util.test.ts +0 -25
- package/src/regex-util.ts +0 -11
- package/src/single-threshold-promise.test.ts +0 -91
- package/src/single-threshold-promise.ts +0 -56
- package/src/singleton/async.test.ts +0 -122
- package/src/singleton/async.ts +0 -90
- package/src/singleton/pattern.test.ts +0 -16
- package/src/singleton/pattern.ts +0 -44
- package/src/string-util.test.ts +0 -18
- package/src/string-util.ts +0 -18
- package/src/time-util.test.ts +0 -89
- package/src/time-util.ts +0 -98
- package/src/timeout.test.ts +0 -65
- package/src/timeout.ts +0 -16
- package/src/type-util.test.ts +0 -20
- package/src/type-util.ts +0 -54
- package/src/types/any-function/index.ts +0 -1
- package/src/types/any-function/no-params.ts +0 -1
- package/src/types/any-function/promise-no-params.ts +0 -1
- package/src/types/any-function/promise.ts +0 -1
- package/src/types/types.d.ts +0 -2
- /package/{lib → dist}/array-util.d.ts +0 -0
- /package/{lib → dist}/class-factory-pattern.d.ts +0 -0
- /package/{lib → dist}/express/error-handler.d.ts +0 -0
- /package/{lib → dist}/joi-util.d.ts +0 -0
- /package/{lib → dist}/object-util.d.ts +0 -0
- /package/{lib → dist}/regex-util.d.ts +0 -0
- /package/{lib → dist}/regex-util.d.ts.map +0 -0
- /package/{lib → dist}/string-util.d.ts +0 -0
- /package/{lib → dist}/string-util.d.ts.map +0 -0
- /package/{lib → dist}/time-util.d.ts +0 -0
- /package/{lib → dist}/time-util.d.ts.map +0 -0
- /package/{lib → dist}/timeout.d.ts +0 -0
- /package/{lib → dist}/timeout.d.ts.map +0 -0
- /package/{lib → dist}/type-util.d.ts +0 -0
- /package/{lib → dist}/type-util.d.ts.map +0 -0
package/src/array-util.test.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { arrayUtil } from 'src/array-util'
|
|
2
|
-
|
|
3
|
-
describe('arrayUtil', () => {
|
|
4
|
-
describe('notEmpty', () => {
|
|
5
|
-
it.each([
|
|
6
|
-
[
|
|
7
|
-
[1, 2, undefined, 4, null, 5],
|
|
8
|
-
[1, 2, 4, 5],
|
|
9
|
-
],
|
|
10
|
-
[
|
|
11
|
-
['', 0],
|
|
12
|
-
['', 0],
|
|
13
|
-
],
|
|
14
|
-
[[undefined, null], []],
|
|
15
|
-
[[undefined, 4, null], [4]],
|
|
16
|
-
[
|
|
17
|
-
[[1], [2], [undefined], [4], [null], [5]],
|
|
18
|
-
[[1], [2], [undefined], [4], [null], [5]],
|
|
19
|
-
],
|
|
20
|
-
[
|
|
21
|
-
[{ a: 1 }, { a: 2 }, { a: undefined }, { a: 4 }, { a: null }, { a: 5 }],
|
|
22
|
-
[{ a: 1 }, { a: 2 }, { a: undefined }, { a: 4 }, { a: null }, { a: 5 }],
|
|
23
|
-
],
|
|
24
|
-
])('%#. should filter not empty values from array %s to be %s', (arr: any, expected) => {
|
|
25
|
-
expect(arr.filter(arrayUtil.notEmpty)).toEqual(expected)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
describe('notFalsy', () => {
|
|
30
|
-
it.each([
|
|
31
|
-
[
|
|
32
|
-
[1, 2, undefined, 4, null, 5],
|
|
33
|
-
[1, 2, 4, 5],
|
|
34
|
-
],
|
|
35
|
-
[['', 0], []],
|
|
36
|
-
[[undefined, null], []],
|
|
37
|
-
[[undefined, 4, null], [4]],
|
|
38
|
-
[
|
|
39
|
-
[[1], [2], [undefined], [4], [null], [5]],
|
|
40
|
-
[[1], [2], [undefined], [4], [null], [5]],
|
|
41
|
-
],
|
|
42
|
-
[
|
|
43
|
-
[{ a: 1 }, { a: 2 }, { a: undefined }, { a: 4 }, { a: null }, { a: 5 }],
|
|
44
|
-
[{ a: 1 }, { a: 2 }, { a: undefined }, { a: 4 }, { a: null }, { a: 5 }],
|
|
45
|
-
],
|
|
46
|
-
])('%#. should filter not empty values from array %s to be %s', (arr: any, expected) => {
|
|
47
|
-
expect(arr.filter(arrayUtil.notFalsy)).toEqual(expected)
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
})
|
package/src/array-util.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export const arrayUtil = {
|
|
2
|
-
/**
|
|
3
|
-
* Check if array element is not empty
|
|
4
|
-
* @template T
|
|
5
|
-
* @param {T | null | undefined} value
|
|
6
|
-
* @return {value is T}
|
|
7
|
-
* @example
|
|
8
|
-
* const notEmptyArray = [0, 1, 2, null, undefined, ''].filter(arrayUtil.notEmpty)
|
|
9
|
-
* console.log(notEmptyArray)// [0, 1, 2, '']
|
|
10
|
-
*/
|
|
11
|
-
notEmpty: <T>(value: T | null | undefined): value is T => {
|
|
12
|
-
return value !== null && value !== undefined
|
|
13
|
-
},
|
|
14
|
-
/**
|
|
15
|
-
* Check if array element is not falsy
|
|
16
|
-
* @template T
|
|
17
|
-
* @param {T | null | undefined} value
|
|
18
|
-
* @return {value is T}
|
|
19
|
-
* @example
|
|
20
|
-
* const notFalsyArray = [0, 1, 2, null, undefined, ''].filter(arrayUtil.notFalsy)
|
|
21
|
-
* console.log(notFalsyArray)// [1, 2]
|
|
22
|
-
*/
|
|
23
|
-
notFalsy: <T>(value: T | null | undefined): value is T => {
|
|
24
|
-
return !!value
|
|
25
|
-
},
|
|
26
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { classFactoryPattern } from 'src/class-factory-pattern'
|
|
2
|
-
|
|
3
|
-
describe('factoryPattern', () => {
|
|
4
|
-
const fakeClassMock = jest.fn()
|
|
5
|
-
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
fakeClassMock.mockImplementation((a: string) => {
|
|
8
|
-
return { a }
|
|
9
|
-
})
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
afterEach(() => jest.resetAllMocks())
|
|
13
|
-
|
|
14
|
-
it('should return result from factory function', () => {
|
|
15
|
-
const expectedAValue = 'test'
|
|
16
|
-
const factoryPatternImplementation = classFactoryPattern(fakeClassMock)
|
|
17
|
-
expect(fakeClassMock).not.toHaveBeenCalled()
|
|
18
|
-
const result = factoryPatternImplementation(expectedAValue)
|
|
19
|
-
expect(fakeClassMock).toHaveBeenCalledTimes(1)
|
|
20
|
-
expect(result.a).toEqual(expectedAValue)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('should call factory function every time the factory implementation is called', () => {
|
|
24
|
-
const expectedAValue = 'test'
|
|
25
|
-
const factoryPatternImplementation = classFactoryPattern(fakeClassMock)
|
|
26
|
-
expect(fakeClassMock).not.toHaveBeenCalled()
|
|
27
|
-
const result1 = factoryPatternImplementation(expectedAValue)
|
|
28
|
-
expect(fakeClassMock).toHaveBeenCalledTimes(1)
|
|
29
|
-
expect(result1.a).toEqual(expectedAValue)
|
|
30
|
-
|
|
31
|
-
const result2 = factoryPatternImplementation(expectedAValue)
|
|
32
|
-
expect(fakeClassMock).toHaveBeenCalledTimes(2)
|
|
33
|
-
expect(result2.a).toEqual(expectedAValue)
|
|
34
|
-
|
|
35
|
-
const result3 = factoryPatternImplementation(expectedAValue)
|
|
36
|
-
expect(fakeClassMock).toHaveBeenCalledTimes(3)
|
|
37
|
-
expect(result3.a).toEqual(expectedAValue)
|
|
38
|
-
})
|
|
39
|
-
})
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export type ClassType<T = object> = new (...args: T extends { new (...args: infer P): any } ? P : never[]) => T
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This is a wrapper that easily converts class constructor call (`new className(..constructorParams)`) into function call (`classNameFactory(..constructorParams)`)
|
|
5
|
-
* @param {C} classType
|
|
6
|
-
* @template C
|
|
7
|
-
* @return {(...args: ConstructorParameters<C>) => InstanceType<C>}
|
|
8
|
-
* @example
|
|
9
|
-
* export class SomeClass {
|
|
10
|
-
* protected _a: string
|
|
11
|
-
*
|
|
12
|
-
* constructor(params: { a: string }) {
|
|
13
|
-
* const { a } = params
|
|
14
|
-
* this._a = a
|
|
15
|
-
* }
|
|
16
|
-
* }
|
|
17
|
-
*
|
|
18
|
-
* export const someClassFactory = classFactoryPattern(SomeClass)
|
|
19
|
-
*
|
|
20
|
-
* // using
|
|
21
|
-
* const someClassInstance = someClassFactory({ a: 'test' })
|
|
22
|
-
*/
|
|
23
|
-
export const classFactoryPattern = <C extends ClassType>(
|
|
24
|
-
classType: C
|
|
25
|
-
): ((...args: ConstructorParameters<C>) => InstanceType<C>) => {
|
|
26
|
-
return (...args: ConstructorParameters<C>): InstanceType<C> => {
|
|
27
|
-
return new classType(...args) as InstanceType<C>
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { expressErrorHandler } from 'src/express/error-handler'
|
|
2
|
-
|
|
3
|
-
describe('expressErrorHandler', () => {
|
|
4
|
-
const fake_fn = jest.fn()
|
|
5
|
-
const fake_req = jest.fn()
|
|
6
|
-
const fake_res = jest.fn()
|
|
7
|
-
const fake_next = jest.fn()
|
|
8
|
-
|
|
9
|
-
class FakeExpressController {
|
|
10
|
-
@expressErrorHandler
|
|
11
|
-
async login(_req: any, _res: any, _next: any): Promise<any> {
|
|
12
|
-
return await fake_fn()
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
const fakeExpressControllerInstance = new FakeExpressController()
|
|
16
|
-
|
|
17
|
-
afterEach(() => jest.resetAllMocks())
|
|
18
|
-
|
|
19
|
-
it('should call next with error if async function throws error', async () => {
|
|
20
|
-
const error = new Error()
|
|
21
|
-
fake_fn.mockRejectedValue(error)
|
|
22
|
-
|
|
23
|
-
await fakeExpressControllerInstance.login(fake_req, fake_res, fake_next)
|
|
24
|
-
|
|
25
|
-
expect(fake_fn).toHaveBeenCalledTimes(1)
|
|
26
|
-
expect(fake_req).not.toHaveBeenCalled()
|
|
27
|
-
expect(fake_res).not.toHaveBeenCalled()
|
|
28
|
-
expect(fake_next).toHaveBeenCalledTimes(1)
|
|
29
|
-
expect(fake_next).toHaveBeenCalledOnceWith(error)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('should work as usual if async function resolves promise', async () => {
|
|
33
|
-
const expected = { successful: true }
|
|
34
|
-
fake_fn.mockResolvedValue(expected)
|
|
35
|
-
|
|
36
|
-
const result = await fakeExpressControllerInstance.login(fake_req, fake_res, fake_next)
|
|
37
|
-
|
|
38
|
-
expect(fake_fn).toHaveBeenCalledTimes(1)
|
|
39
|
-
expect(fake_req).not.toHaveBeenCalled()
|
|
40
|
-
expect(fake_res).not.toHaveBeenCalled()
|
|
41
|
-
expect(fake_next).not.toHaveBeenCalled()
|
|
42
|
-
expect(result).toBe(expected)
|
|
43
|
-
})
|
|
44
|
-
})
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wrap async express http request end return promise or call next on catch
|
|
3
|
-
* @param _target
|
|
4
|
-
* @param _key
|
|
5
|
-
* @param descriptor
|
|
6
|
-
* @example
|
|
7
|
-
* export class RootController {
|
|
8
|
-
* /@expressErrorHandler
|
|
9
|
-
* async login(req: Request, res: Response): Promise<void> {
|
|
10
|
-
* const { username, password } = validationUtil().sanitize(req.body, postLoginBodySchema)
|
|
11
|
-
* const result = await authorizationUseCase.login({ username, password })
|
|
12
|
-
* res.json(result)
|
|
13
|
-
* }
|
|
14
|
-
* }
|
|
15
|
-
*/
|
|
16
|
-
export const expressErrorHandler = (_target: any, _key: string, descriptor: TypedPropertyDescriptor<any>): any => {
|
|
17
|
-
const originalMethod = descriptor.value
|
|
18
|
-
descriptor.value = function (): any {
|
|
19
|
-
const next = arguments[2] // eslint-disable-line prefer-rest-params
|
|
20
|
-
|
|
21
|
-
return Promise.resolve(originalMethod.apply(this, arguments)).catch(next) // eslint-disable-line prefer-rest-params
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return descriptor
|
|
25
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export * from 'src/express/error-handler'
|
|
2
|
-
|
|
3
|
-
export * from 'src/singleton/async'
|
|
4
|
-
|
|
5
|
-
export * from 'src/singleton/pattern'
|
|
6
|
-
|
|
7
|
-
export * from 'src/class-factory-pattern'
|
|
8
|
-
|
|
9
|
-
export * from 'src/joi-util'
|
|
10
|
-
|
|
11
|
-
export * from 'src/memoize-factory'
|
|
12
|
-
|
|
13
|
-
export * from 'src/object-util'
|
|
14
|
-
|
|
15
|
-
export * from 'src/regex-util'
|
|
16
|
-
|
|
17
|
-
export * from 'src/single-threshold-promise'
|
|
18
|
-
|
|
19
|
-
export * from 'src/string-util'
|
|
20
|
-
|
|
21
|
-
export * from 'src/time-util'
|
|
22
|
-
|
|
23
|
-
export * from 'src/timeout'
|
|
24
|
-
|
|
25
|
-
export * from 'src/type-util'
|
package/src/joi-util.test.ts
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
2
|
-
import { JoiUtil } from 'src/joi-util'
|
|
3
|
-
|
|
4
|
-
describe('JoiUtil', () => {
|
|
5
|
-
const joiUtil = new JoiUtil()
|
|
6
|
-
|
|
7
|
-
const validObject = { a: 'string', b: true, c: 123, d: new Date() }
|
|
8
|
-
const invalidObject = { invalid: true }
|
|
9
|
-
|
|
10
|
-
const joiSchema = Joi.object().keys({
|
|
11
|
-
a: Joi.string().required(),
|
|
12
|
-
b: Joi.boolean().required(),
|
|
13
|
-
c: Joi.number().required(),
|
|
14
|
-
d: Joi.date().required(),
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
describe('sanitize', () => {
|
|
18
|
-
it('should return valid object', () => {
|
|
19
|
-
const result = joiUtil.sanitize(validObject, joiSchema)
|
|
20
|
-
expect(result).toEqual(validObject)
|
|
21
|
-
})
|
|
22
|
-
it('should return valid object and remove properties that are not defined in schema', () => {
|
|
23
|
-
const result = joiUtil.sanitize({ ...validObject, test: true }, joiSchema)
|
|
24
|
-
expect(result).toEqual(validObject)
|
|
25
|
-
expect(result.test).toBeUndefined()
|
|
26
|
-
})
|
|
27
|
-
it('should throw error if object is not valid, return first error', () => {
|
|
28
|
-
try {
|
|
29
|
-
joiUtil.sanitize(invalidObject, joiSchema)
|
|
30
|
-
expect.fail('test failed')
|
|
31
|
-
} catch (err: any) {
|
|
32
|
-
expect(err.message).toEqual("'a' is required")
|
|
33
|
-
expect(err.payload.details).toEqual([
|
|
34
|
-
{
|
|
35
|
-
context: {
|
|
36
|
-
key: 'a',
|
|
37
|
-
label: 'a',
|
|
38
|
-
},
|
|
39
|
-
message: '"a" is required',
|
|
40
|
-
path: ['a'],
|
|
41
|
-
type: 'any.required',
|
|
42
|
-
},
|
|
43
|
-
])
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
it('should throw error if object is not valid, return all errors', () => {
|
|
47
|
-
try {
|
|
48
|
-
joiUtil.sanitize(invalidObject, joiSchema, { abortEarly: false })
|
|
49
|
-
expect.fail('test failed')
|
|
50
|
-
} catch (err: any) {
|
|
51
|
-
expect(err.message).toEqual("'a' is required. 'b' is required. 'c' is required. 'd' is required")
|
|
52
|
-
expect(err.payload.details).toEqual([
|
|
53
|
-
{
|
|
54
|
-
context: {
|
|
55
|
-
key: 'a',
|
|
56
|
-
label: 'a',
|
|
57
|
-
},
|
|
58
|
-
message: '"a" is required',
|
|
59
|
-
path: ['a'],
|
|
60
|
-
type: 'any.required',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
context: {
|
|
64
|
-
key: 'b',
|
|
65
|
-
label: 'b',
|
|
66
|
-
},
|
|
67
|
-
message: '"b" is required',
|
|
68
|
-
path: ['b'],
|
|
69
|
-
type: 'any.required',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
context: {
|
|
73
|
-
key: 'c',
|
|
74
|
-
label: 'c',
|
|
75
|
-
},
|
|
76
|
-
message: '"c" is required',
|
|
77
|
-
path: ['c'],
|
|
78
|
-
type: 'any.required',
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
context: {
|
|
82
|
-
key: 'd',
|
|
83
|
-
label: 'd',
|
|
84
|
-
},
|
|
85
|
-
message: '"d" is required',
|
|
86
|
-
path: ['d'],
|
|
87
|
-
type: 'any.required',
|
|
88
|
-
},
|
|
89
|
-
])
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
describe('validate', () => {
|
|
95
|
-
it('should return valid object', () => {
|
|
96
|
-
const result = joiUtil.validate(validObject, joiSchema)
|
|
97
|
-
expect(result).toEqual(validObject)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('should throw error if there are unknown properties', () => {
|
|
101
|
-
try {
|
|
102
|
-
joiUtil.validate({ ...validObject, test: true }, joiSchema)
|
|
103
|
-
expect.fail('test failed')
|
|
104
|
-
} catch (err: any) {
|
|
105
|
-
expect(err.message).toEqual("'test' is not allowed")
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
it('should throw error if object is not valid, return first error', () => {
|
|
109
|
-
try {
|
|
110
|
-
joiUtil.validate(invalidObject, joiSchema)
|
|
111
|
-
expect.fail('test failed')
|
|
112
|
-
} catch (err: any) {
|
|
113
|
-
expect(err.message).toEqual("'a' is required")
|
|
114
|
-
expect(err.payload.details).toEqual([
|
|
115
|
-
{
|
|
116
|
-
context: {
|
|
117
|
-
key: 'a',
|
|
118
|
-
label: 'a',
|
|
119
|
-
},
|
|
120
|
-
message: '"a" is required',
|
|
121
|
-
path: ['a'],
|
|
122
|
-
type: 'any.required',
|
|
123
|
-
},
|
|
124
|
-
])
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
it('should throw error if object is not valid, return all errors', () => {
|
|
128
|
-
try {
|
|
129
|
-
joiUtil.validate(invalidObject, joiSchema, { abortEarly: false })
|
|
130
|
-
expect.fail('test failed')
|
|
131
|
-
} catch (err: any) {
|
|
132
|
-
expect(err.message).toEqual(
|
|
133
|
-
"'a' is required. 'b' is required. 'c' is required. 'd' is required. 'invalid' is not allowed"
|
|
134
|
-
)
|
|
135
|
-
expect(err.payload.details).toEqual([
|
|
136
|
-
{
|
|
137
|
-
context: {
|
|
138
|
-
key: 'a',
|
|
139
|
-
label: 'a',
|
|
140
|
-
},
|
|
141
|
-
message: '"a" is required',
|
|
142
|
-
path: ['a'],
|
|
143
|
-
type: 'any.required',
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
context: {
|
|
147
|
-
key: 'b',
|
|
148
|
-
label: 'b',
|
|
149
|
-
},
|
|
150
|
-
message: '"b" is required',
|
|
151
|
-
path: ['b'],
|
|
152
|
-
type: 'any.required',
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
context: {
|
|
156
|
-
key: 'c',
|
|
157
|
-
label: 'c',
|
|
158
|
-
},
|
|
159
|
-
message: '"c" is required',
|
|
160
|
-
path: ['c'],
|
|
161
|
-
type: 'any.required',
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
context: {
|
|
165
|
-
key: 'd',
|
|
166
|
-
label: 'd',
|
|
167
|
-
},
|
|
168
|
-
message: '"d" is required',
|
|
169
|
-
path: ['d'],
|
|
170
|
-
type: 'any.required',
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
context: {
|
|
174
|
-
child: 'invalid',
|
|
175
|
-
key: 'invalid',
|
|
176
|
-
label: 'invalid',
|
|
177
|
-
value: true,
|
|
178
|
-
},
|
|
179
|
-
message: '"invalid" is not allowed',
|
|
180
|
-
path: ['invalid'],
|
|
181
|
-
type: 'object.unknown',
|
|
182
|
-
},
|
|
183
|
-
])
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('should allow unknown if flag is set and call logger with warn message', () => {
|
|
188
|
-
const result = joiUtil.validate({ ...validObject, unknownProp: 'test' }, joiSchema, { allowUnknown: true })
|
|
189
|
-
expect(result).toEqual({ ...validObject, unknownProp: 'test' })
|
|
190
|
-
})
|
|
191
|
-
})
|
|
192
|
-
})
|
package/src/joi-util.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { ObjectSchema, Schema, ValidationOptions } from 'joi'
|
|
2
|
-
|
|
3
|
-
export class ErrorWithPayload<T> extends Error {
|
|
4
|
-
payload: T
|
|
5
|
-
|
|
6
|
-
constructor(message: string, payload: T) {
|
|
7
|
-
super(message)
|
|
8
|
-
this.payload = payload
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* This is a simple wrapper around Joi validation with two functions exposed validate and sanitize. If object is not valid function throws an error.
|
|
14
|
-
* @example
|
|
15
|
-
* type SomeType = {
|
|
16
|
-
* a: string
|
|
17
|
-
* b: number
|
|
18
|
-
* }
|
|
19
|
-
* const someSchema = Joi.object<SomeType>().keys({
|
|
20
|
-
* a: Joi.string().required(),
|
|
21
|
-
* b: Joi.number().required(),
|
|
22
|
-
* }).required()
|
|
23
|
-
*
|
|
24
|
-
* const joiUtil = new JoiUtil()
|
|
25
|
-
*
|
|
26
|
-
* // using
|
|
27
|
-
* const invalidObject = joiUtil.validate({}, someSchema) // validate throws an error
|
|
28
|
-
* const validObject = joiUtil.validate({ a: 'a', b: 1 }, someSchema)
|
|
29
|
-
*/
|
|
30
|
-
export class JoiUtil {
|
|
31
|
-
/**
|
|
32
|
-
* Validate and clean object
|
|
33
|
-
* @template T
|
|
34
|
-
* @template Joi
|
|
35
|
-
* @param {any} objectToValidate
|
|
36
|
-
* @param {Joi.Schema | Joi.ObjectSchema<T>} schema
|
|
37
|
-
* @param {validationOptions} [validationOptions]
|
|
38
|
-
* @returns {T} expected object after validation
|
|
39
|
-
*/
|
|
40
|
-
sanitize<T>(objectToValidate: any, schema: Schema | ObjectSchema<T>, validationOptions?: ValidationOptions): T {
|
|
41
|
-
return this._validate<T>(objectToValidate, schema, { ...validationOptions, stripUnknown: true })
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Only validate properties specified in validation schema
|
|
46
|
-
* @template T
|
|
47
|
-
* @template Joi
|
|
48
|
-
* @param {any} objectToValidate
|
|
49
|
-
* @param {Joi.Schema | Joi.ObjectSchema<T>} schema
|
|
50
|
-
* @param {validationOptions} [validationOptions]
|
|
51
|
-
* @returns {T} expected object after validation
|
|
52
|
-
*/
|
|
53
|
-
validate<T>(objectToValidate: any, schema: Schema | ObjectSchema<T>, validationOptions?: ValidationOptions): T {
|
|
54
|
-
return this._validate<T>(objectToValidate, schema, validationOptions)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
protected _validate<T>(objectToValidate: any, schema: Schema | ObjectSchema<T>, validationOptions?: ValidationOptions): T {
|
|
58
|
-
const { error: validationError, value } = schema.validate(objectToValidate, validationOptions)
|
|
59
|
-
if (validationError) {
|
|
60
|
-
throw new ErrorWithPayload(validationError.message.split('"').join("'"), validationError)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return value as T
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { memoizeFactory } from 'src/memoize-factory'
|
|
2
|
-
|
|
3
|
-
describe('memoizeFactory', () => {
|
|
4
|
-
const fake_factoryFn = jest.fn()
|
|
5
|
-
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
fake_factoryFn.mockImplementation((a: number, b: number): number => {
|
|
8
|
-
return a + b
|
|
9
|
-
})
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
jest.resetAllMocks()
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('should only call factory function once if the same argument is passed', () => {
|
|
17
|
-
const memoizedImplementation = memoizeFactory(fake_factoryFn)
|
|
18
|
-
expect(fake_factoryFn).not.toHaveBeenCalled()
|
|
19
|
-
|
|
20
|
-
expect(memoizedImplementation(1, 2)).toEqual(3)
|
|
21
|
-
expect(fake_factoryFn).toHaveBeenCalledTimes(1)
|
|
22
|
-
|
|
23
|
-
expect(memoizedImplementation(1, 2)).toEqual(3)
|
|
24
|
-
expect(fake_factoryFn).toHaveBeenCalledTimes(1)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('should call factory as many times as it is called if the arguments are different', () => {
|
|
28
|
-
const memoizedImplementation = memoizeFactory(fake_factoryFn)
|
|
29
|
-
expect(fake_factoryFn).not.toHaveBeenCalled()
|
|
30
|
-
|
|
31
|
-
expect(memoizedImplementation(1, 2)).toEqual(3)
|
|
32
|
-
expect(fake_factoryFn).toHaveBeenCalledTimes(1)
|
|
33
|
-
|
|
34
|
-
expect(memoizedImplementation(1, 3)).toEqual(4)
|
|
35
|
-
expect(fake_factoryFn).toHaveBeenCalledTimes(2)
|
|
36
|
-
|
|
37
|
-
expect(memoizedImplementation(1, 4)).toEqual(5)
|
|
38
|
-
expect(fake_factoryFn).toHaveBeenCalledTimes(3)
|
|
39
|
-
})
|
|
40
|
-
})
|
package/src/memoize-factory.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { AnyFunction } from 'src/types/any-function'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This is a simple implementation of memoize function that caches result against the parameter passed that are passed to the
|
|
5
|
-
* function so it never runs the same calculation twice.
|
|
6
|
-
* @template F
|
|
7
|
-
* @template R
|
|
8
|
-
* @param {F} factoryFn
|
|
9
|
-
* @return {F: AnyFunction<R>}
|
|
10
|
-
* @example
|
|
11
|
-
* export const sumTwoNumbersMemoize = memoizeFactory((a:number, b:number): number => a + b)
|
|
12
|
-
*
|
|
13
|
-
* // using
|
|
14
|
-
* sumTwoNumbersMemoize(5 + 10) // 15
|
|
15
|
-
*/
|
|
16
|
-
export const memoizeFactory = <F extends AnyFunction<R>, R>(factoryFn: F): F => {
|
|
17
|
-
const cache: { [k: string]: R } = {}
|
|
18
|
-
|
|
19
|
-
return ((...args: Parameters<F>): R => {
|
|
20
|
-
const key = JSON.stringify(args)
|
|
21
|
-
if (key in cache) {
|
|
22
|
-
return cache[key]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (cache[key] = factoryFn(...args))
|
|
26
|
-
}) as F
|
|
27
|
-
}
|