on ad8630b help sign in
bridge/src/form/validators.js
142 lines 4.6 kB history raw
1import { some } from 'lodash';
3import {
4 validateNotEmpty,
5 validatePatq,
6 validateMnemonic,
7 validatePoint,
8 validateMaximumPatpByteLength,
9 validateOneOf,
10 validateHexString,
11 validateHexLength,
12 validateEthereumAddress,
13 validateGreaterThan,
14 validateEmail,
15 validateNotNullAddress,
16} from 'lib/validators';
17import isPromise from 'lib/isPromise';
19// iterate over validators, exiting early if there's an error
20const buildValidator = (validators = []) => value => {
21 for (const validator of validators) {
22 try {
23 const error = validator(value);
24 if (error) {
25 return error;
26 }
27 } catch (error) {
28 console.error(error);
29 return error.message;
30 }
31 }
32};
34// maps a validator across an array of values
35export const buildArrayValidator = validator => async values =>
36 await Promise.all(values.map(validator));
38// error object has errors if some of its fields are
39// 1) an array with any defined values
40// 2) any defined values
41export const hasErrors = iter =>
42 some(iter, v => (Array.isArray(v) ? hasErrors(v) : v !== undefined));
44export const buildPatqValidator = (validators = []) =>
45 buildValidator([validateNotEmpty, validatePatq, ...validators]);
46export const buildMnemonicValidator = () =>
47 buildValidator([validateNotEmpty, validateMnemonic]);
48export const buildCheckboxValidator = mustBe =>
49 buildValidator([
50 validateOneOf(mustBe !== undefined ? [mustBe] : [true, false]),
51 ]);
52export const buildPassphraseValidator = () => buildValidator([]);
53// TODO: validate hdpath format
54export const buildHdPathValidator = () => buildValidator([validateNotEmpty]);
55export const buildPointValidator = (size = 4, validators = []) =>
56 buildValidator([
57 validateNotEmpty,
58 validateMaximumPatpByteLength(size),
59 validatePoint,
60 ...validators,
61 ]);
62export const buildSelectValidator = options =>
63 buildValidator([validateOneOf(options.map(option => option.value))]);
64export const buildHexValidator = length =>
65 buildValidator([
66 validateNotEmpty,
67 validateHexString,
68 validateHexLength(length),
69 ]);
70export const buildUploadValidator = () => buildValidator([validateNotEmpty]);
71export const buildAddressValidator = () =>
72 buildValidator([
73 validateNotEmpty,
74 validateHexString,
75 validateHexLength(40),
76 validateNotNullAddress,
77 validateEthereumAddress,
78 ]);
79export const buildNumberValidator = (min = 0) =>
80 buildValidator([validateGreaterThan(min)]);
81export const buildEmailValidator = (validators = []) =>
82 buildValidator([validateNotEmpty, validateEmail, ...validators]);
84// the form validator is the composition of all of the field validators
85// plus an additional form validator function
86export const composeValidator = (
87 fieldValidators = {},
88 // the default form validator just returns field-level validations
89 formValidator = (values, errors) => errors
90) => {
91 const names = Object.keys(fieldValidators);
93 const fieldLevelValidators = names.map(name => value =>
94 fieldValidators[name](value)
95 );
97 // async reduce errors per-field into an errors object
98 const fieldLevelValidator = values => {
99 const errorsOrPromises = names.map((name, i) =>
100 fieldLevelValidators[i](values[name])
101 );
103 const reduce = errors =>
104 names.reduce(
105 (memo, name, i) => ({
106 ...memo,
107 [name]: errors[i],
108 }),
109 {}
110 );
112 if (some(errorsOrPromises, isPromise)) {
113 // if any of these results are a promise, await them all then reduce
114 return Promise.all(errorsOrPromises).then(reduce);
115 }
117 // otherwise return immediately
118 return reduce(errorsOrPromises);
119 };
121 // the final-form `validate` function
122 // NOTE: if we return a Promise to final-form it will toggle the `validating`
123 // state, which is expected. If our promise resolves immediately, however,
124 // that means our `validating` state flickers the UI and it looks pretty bad.
125 // The solution is to conditionally return a promise only when necessary.
126 return values => {
127 // ask for field-level errors
128 const errorsOrPromise = fieldLevelValidator(values);
130 // pass the current values and their validity to the form-level validator
131 // that can implement conditional logic and more complex validations
132 const runFormValidator = errors => formValidator(values, errors);
134 if (isPromise(errorsOrPromise)) {
135 // if promise, promise
136 return errorsOrPromise.then(runFormValidator);
137 }
139 // otherwise, it's an errors object
140 return runFormValidator(errorsOrPromise);
141 };
142};