-
Notifications
You must be signed in to change notification settings - Fork 0
/
validation.ts
122 lines (114 loc) · 3.73 KB
/
validation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import type { Errors, Type } from "io-ts";
import * as t from "io-ts";
import type { Either } from "fp-ts/es6/Either";
import { getErrorDetailReporter } from "./reporters/ErrorDetailReporter";
import { badRequestError, type EnonicError } from "enonic-fp/errors";
import { fromEither, IOEither, mapLeft } from "fp-ts/es6/IOEither";
import { pipe } from "fp-ts/es6/function";
import type { LocalizeWithPrefixParams } from "enonic-fp/controller";
export function validate<A, O = A, I = unknown>(
a: Type<A, O, I>,
localizeParams?: LocalizeWithPrefixParams
): (i: I) => IOEither<EnonicError, A> {
return (i: I): IOEither<EnonicError, A> =>
normalizeDecoded(a.decode(i), localizeParams);
}
export function normalizeDecoded<A>(
decoded: Either<Errors, A>,
localizeParams?: LocalizeWithPrefixParams
): IOEither<EnonicError, A> {
return pipe(
decoded,
fromEither,
mapLeft(() => ({
...badRequestError,
errors: getErrorDetailReporter(localizeParams).report(decoded),
}))
);
}
export interface RegexpValidatedStringProps<A extends boolean = false> {
readonly regexp: RegExp;
readonly isNullable?: A;
}
export function RegexpValidatedString(
props: RegexpValidatedStringProps
): t.Type<string, string>;
export function RegexpValidatedString(
props: RegexpValidatedStringProps<true>
): t.Type<string | undefined, string | undefined>;
export function RegexpValidatedString<A extends string | undefined>({
regexp,
isNullable = false,
}: RegexpValidatedStringProps<boolean>): t.Type<A, A> {
return new t.Type<A, A, unknown>(
"RegexpValidatedString",
(u): u is A =>
(typeof u === "string" && regexp.test(u)) ||
(isNullable && u === undefined),
(u, c) => {
return (typeof u === "string" && regexp.test(u)) ||
(isNullable && u === undefined)
? t.success(u as A)
: t.failure(u, c);
},
t.identity
);
}
export interface MaxLengthValidatedStringProps<A extends boolean = false> {
readonly maxLength: number;
readonly isNullable?: A;
}
export function MaxLengthValidatedString(
props: MaxLengthValidatedStringProps
): t.Type<string, string>;
export function MaxLengthValidatedString(
props: MaxLengthValidatedStringProps<true>
): t.Type<string | undefined, string | undefined>;
export function MaxLengthValidatedString<A extends string | undefined>({
maxLength,
isNullable = false,
}: MaxLengthValidatedStringProps<boolean>): t.Type<A, A> {
return new t.Type<A, A, unknown>(
"MaxLengthValidatedString",
(u): u is A =>
(typeof u === "string" && u.length <= maxLength) ||
(isNullable && u === undefined),
(u, c) => {
return (typeof u === "string" && u.length <= maxLength) ||
(isNullable && u === undefined)
? t.success(u as A)
: t.failure(u, c);
},
t.identity
);
}
export interface MinMaxValidatedNumberProps<A extends boolean = false> {
readonly min?: number;
readonly max?: number;
readonly isNullable?: A;
}
export function MinMaxValidatedNumber(
props: MinMaxValidatedNumberProps
): t.Type<string, string>;
export function MinMaxValidatedNumber(
props: MinMaxValidatedNumberProps<true>
): t.Type<string | undefined, string | undefined>;
export function MinMaxValidatedNumber<A extends number | undefined>({
min = Number.NEGATIVE_INFINITY,
max = Number.POSITIVE_INFINITY,
isNullable = false,
}: MinMaxValidatedNumberProps<boolean>): t.Type<A, A> {
return new t.Type<A, A, unknown>(
"MinMaxValidatedNumber",
(u): u is A =>
(t.number.is(u) && min <= u && u <= max) ||
(isNullable && u === undefined),
(u, c) => {
return (t.number.is(u) && min <= u && u <= max) ||
(isNullable && u === undefined)
? t.success(u as A)
: t.failure(u, c);
},
t.identity
);
}