All files / src/validator validator.ts

100% Statements 44/44
94.29% Branches 33/35
100% Functions 5/5
100% Lines 39/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 1565x                   5x         5x         5x 3x                 5x 3x                           5x   214x 215x     221x   428x     214x 51x 51x 18x                               196x 11x   11x         186x   33x 33x 32x           32x 10x 10x       22x         1x     1x       23x         186x   11x 11x 4x                     7x       175x           5x     5x     5x                
import {
    ValidatorRule,
    ValidatorMessages,
    ValidatorData,
    ValidatorContext,
    ValidatorRuleMap,
    ValidatorMessageGenerator,
    ValueMap,
    ValidatorRules,
} from '../types';
import { validatorRules as importedValidatorRules } from '../rules';
 
/**
 * Validator rules to be used by validator
 */
let validatorRules = importedValidatorRules;
 
/**
 * Returns current validator rules in use
 */
export const getValidatorRules = (): ValidatorRuleMap => {
    return validatorRules;
};
 
/**
 * Adds a custom validator rule to the validator. If the rule is
 * already defined, the new rule will override the original.
 *
 * @param rule new rule to be added
 */
export const addValidatorRule = (key: string, rule: ValidatorRule): void => {
    validatorRules = {
        ...validatorRules,
        [key]: rule,
    };
};
 
/**
 * Validate a field using the provided rules
 *
 * @param valueKey key of value to be validated
 * @param values values of all fields
 * @param targetRules validator rules to use
 * @param customMessages custom validator messages
 */
export const validate = (
    valueKey: string,
    Ivalues: ValueMap = {},
    targetRules: {
        readonly [name: string]: any;
    } = {},
    customMessages: ValidatorMessages = {},
): ValidatorData => {
    const { required, custom, ...restRules } = targetRules;
 
    // Execute required rule first (if it exists)
    if (required) {
        const response = validatorRules.required(valueKey, values);
        if (response) {
            return {
                ...response,
                ...('required' in customMessages
                    ? {
                          message: formatCustomMessage(
                              customMessages['required'],
                              valueKey,
                              values,
                          ),
                      }
                    : {}),
            };
        }
    }
 
    // Execute custom validator rule second (if it exists)
    if (custom) {
        const response = custom(valueKey, values);
        // Only return if there is a response to return
        if (response) return response;
    }
 
    // Execute rest rules
    let cachedValidatorData: ValidatorData;
    Object.keys(restRules).some(
        (ruleKey: keyof Omit<ValidatorRules, 'custom'>) => {
            const criteria = targetRules[ruleKey];
            if (ruleKey in validatorRules) {
                const data: ValidatorData = {
                    name: ruleKey,
                    ...validatorRules[ruleKey](valueKey, values, criteria),
                };
 
                // Break early if danger context is encountered
                if (data && data.context === ValidatorContext.Danger) {
                    cachedValidatorData = data;
                    return true;
                }
 
                // Cache first warning context, don't break because there might be an error later on
                if (
                    data &&
                    data.context === ValidatorContext.Warning &&
                    !cachedValidatorData
                ) {
                    cachedValidatorData = data;
                }
            } else {
                console.warn(`Rule "${ruleKey}" does not exist.`);
            }
 
            // Keep iterating
            return false;
        },
    );
 
    // If we have a cached validatorData, return it
    if (cachedValidatorData) {
        // Use custom error message if available
        const ruleKey = cachedValidatorData.name;
        if (ruleKey in customMessages) {
            return {
                ...cachedValidatorData,
                message: formatCustomMessage(
                    customMessages[ruleKey],
                    valueKey,
                    values,
                    targetRules[ruleKey],
                ),
            };
        }
 
        return cachedValidatorData;
    }
 
    // If we don't have a cached response, assume validator is successful
    return {
        context: ValidatorContext.Success,
        message: undefined,
    };
};
 
const formatCustomMessage = (
    customMessage: ValidatorMessageGenerator | string,
    valueKey: string,
    Ivalues: ValueMap = {},
    criteria?: any,
) => {
    return customMessage instanceof Function
        ? ((customMessage as ValidatorMessageGenerator)(
              valueKey,
              values,
              criteria,
          ) as string)
        : (customMessage as string);
};