2021-07-26 05:50:46 +00:00
|
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
|
|
|
|
2021-01-04 10:16:08 +00:00
|
|
|
import {
|
2021-07-26 05:50:46 +00:00
|
|
|
ValidationTypes,
|
2021-01-04 10:16:08 +00:00
|
|
|
ValidationResponse,
|
|
|
|
|
Validator,
|
|
|
|
|
} from "../constants/WidgetValidation";
|
|
|
|
|
import _, {
|
|
|
|
|
isObject,
|
2021-02-16 10:29:08 +00:00
|
|
|
isPlainObject,
|
2021-01-04 10:16:08 +00:00
|
|
|
isString,
|
2021-07-02 07:09:17 +00:00
|
|
|
startsWith,
|
2021-01-04 10:16:08 +00:00
|
|
|
toString,
|
|
|
|
|
} from "lodash";
|
|
|
|
|
import { WidgetProps } from "../widgets/BaseWidget";
|
2021-07-26 05:50:46 +00:00
|
|
|
|
2021-01-04 10:16:08 +00:00
|
|
|
import moment from "moment";
|
2021-07-26 05:50:46 +00:00
|
|
|
import { ValidationConfig } from "constants/PropertyControlConstants";
|
|
|
|
|
import evaluate from "./evaluate";
|
2021-01-04 10:16:08 +00:00
|
|
|
|
2021-07-28 06:01:09 +00:00
|
|
|
/**
|
|
|
|
|
* REF: https://github.com/angular/angular/blob/master/packages/core/src/sanitization/url_sanitizer.ts
|
|
|
|
|
* A pattern that recognizes a commonly useful subset of URLs that are safe.
|
|
|
|
|
*
|
|
|
|
|
* This regular expression matches a subset of URLs that will not cause script
|
|
|
|
|
* execution if used in URL context within a HTML document. Specifically, this
|
|
|
|
|
* regular expression matches if (comment from here on and regex copied from
|
|
|
|
|
* Soy's EscapingConventions):
|
|
|
|
|
* (1) Either an allowed protocol (http, https, mailto or ftp).
|
|
|
|
|
* (2) or no protocol. A protocol must be followed by a colon. The below
|
|
|
|
|
* allows that by allowing colons only after one of the characters [/?#].
|
|
|
|
|
* A colon after a hash (#) must be in the fragment.
|
|
|
|
|
* Otherwise, a colon after a (?) must be in a query.
|
|
|
|
|
* Otherwise, a colon after a single solidus (/) must be in a path.
|
|
|
|
|
* Otherwise, a colon after a double solidus (//) must be in the authority
|
|
|
|
|
* (before port).
|
|
|
|
|
*
|
|
|
|
|
* The pattern disallows &, used in HTML entity declarations before
|
|
|
|
|
* one of the characters in [/?#]. This disallows HTML entities used in the
|
|
|
|
|
* protocol name, which should never happen, e.g. "http" for "http".
|
|
|
|
|
* It also disallows HTML entities in the first path part of a relative path,
|
|
|
|
|
* e.g. "foo<bar/baz". Our existing escaping functions should not produce
|
|
|
|
|
* that. More importantly, it disallows masking of a colon,
|
|
|
|
|
* e.g. "javascript:...".
|
|
|
|
|
*
|
|
|
|
|
* This regular expression was taken from the Closure sanitization library.
|
|
|
|
|
*/
|
|
|
|
|
const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^&:/?#]*(?:[/?#]|$))/gi;
|
|
|
|
|
|
|
|
|
|
/** A pattern that matches safe data URLs. Only matches image, video and audio types. */
|
|
|
|
|
const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+\/]+=*$/i;
|
|
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
function validatePlainObject(
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: Record<string, unknown>,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-03-24 12:12:24 +00:00
|
|
|
) {
|
2021-07-26 05:50:46 +00:00
|
|
|
if (config.params?.allowedKeys) {
|
|
|
|
|
let _valid = true;
|
|
|
|
|
const _messages: string[] = [];
|
|
|
|
|
config.params.allowedKeys.forEach((entry) => {
|
|
|
|
|
if (value.hasOwnProperty(entry.name)) {
|
|
|
|
|
const { isValid, message, parsed } = validate(
|
|
|
|
|
entry,
|
|
|
|
|
value[entry.name],
|
|
|
|
|
props,
|
|
|
|
|
);
|
2021-03-13 14:24:45 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (!isValid) {
|
|
|
|
|
value[entry.name] = parsed;
|
|
|
|
|
_valid = isValid;
|
|
|
|
|
message &&
|
|
|
|
|
_messages.push(
|
|
|
|
|
`Value of key: ${entry.name} is invalid: ${message}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else if (entry.params?.required) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: value,
|
|
|
|
|
message: `Missing required key: ${entry.name}`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
});
|
|
|
|
|
if (_valid) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
|
|
|
|
};
|
2021-01-04 10:16:08 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config.params?.default || value,
|
|
|
|
|
message: _messages.join(" "),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
function validateArray(
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown[],
|
|
|
|
|
props: Record<string, unknown>,
|
|
|
|
|
) {
|
|
|
|
|
const whiteList = config.params?.allowedValues;
|
|
|
|
|
if (whiteList) {
|
|
|
|
|
value.forEach((entry) => {
|
|
|
|
|
if (!whiteList.includes(entry)) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: value,
|
|
|
|
|
message: `Disallowed value: ${entry}`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const children = config.params?.children;
|
|
|
|
|
let _isValid = true;
|
|
|
|
|
const _messages: string[] = [];
|
|
|
|
|
if (children) {
|
|
|
|
|
value.forEach((entry, index) => {
|
|
|
|
|
const validation = validate(children, entry, props);
|
|
|
|
|
if (!validation.isValid) {
|
|
|
|
|
_isValid = false;
|
|
|
|
|
_messages.push(
|
|
|
|
|
`Invalid entry at index: ${index}. ${validation.message}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return { isValid: _isValid, parsed: value, message: _messages.join(" ") };
|
|
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
export const validate = (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
|
|
|
|
) => {
|
|
|
|
|
const _result = VALIDATORS[config.type as ValidationTypes](
|
|
|
|
|
config,
|
|
|
|
|
value,
|
|
|
|
|
props,
|
|
|
|
|
);
|
|
|
|
|
return _result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const WIDGET_TYPE_VALIDATION_ERROR =
|
|
|
|
|
"This value does not evaluate to type"; // TODO: Lot's of changes in validations.ts file
|
|
|
|
|
|
|
|
|
|
export const VALIDATORS: Record<ValidationTypes, Validator> = {
|
|
|
|
|
[ValidationTypes.TEXT]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
|
|
|
|
): ValidationResponse => {
|
|
|
|
|
if (value === undefined || value === null) {
|
|
|
|
|
if (config.params && config.params.required) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || "",
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "string"`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
2021-07-08 10:40:22 +00:00
|
|
|
isValid: true,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || "",
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
let parsed = value;
|
2021-07-26 05:50:46 +00:00
|
|
|
|
|
|
|
|
if (isObject(value)) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: JSON.stringify(value, null, 2),
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "string"`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
|
|
|
|
|
const isValid = isString(parsed);
|
2021-01-04 10:16:08 +00:00
|
|
|
if (!isValid) {
|
|
|
|
|
try {
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed = toString(parsed);
|
2021-01-04 10:16:08 +00:00
|
|
|
} catch (e) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || "",
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "string"`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
if (config.params?.allowedValues) {
|
|
|
|
|
if (!config.params?.allowedValues.includes((parsed as string).trim())) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || "",
|
|
|
|
|
message: "Value is not allowed",
|
2021-01-04 10:16:08 +00:00
|
|
|
isValid: false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-02 09:55:50 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (config.params?.regex && !config.params?.regex.test(parsed as string)) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || "",
|
|
|
|
|
message: `Value does not match expected regex: ${config.params?.regex.source}`,
|
2021-01-04 10:16:08 +00:00
|
|
|
isValid: false,
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed,
|
|
|
|
|
};
|
2021-01-04 10:16:08 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
// TODO(abhinav): The original validation does not make sense fix this.
|
|
|
|
|
[ValidationTypes.REGEX]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-04-23 05:43:13 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
const { isValid, message, parsed } = VALIDATORS[ValidationTypes.TEXT](
|
|
|
|
|
config,
|
2021-04-23 05:43:13 +00:00
|
|
|
value,
|
|
|
|
|
props,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!isValid) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: new RegExp(parsed),
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "regex"`,
|
2021-04-23 05:43:13 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
|
|
|
|
|
return { isValid, parsed, message };
|
2021-04-23 05:43:13 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.NUMBER]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-01-04 10:16:08 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
if (value === undefined || value === null) {
|
|
|
|
|
if (config.params?.required) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config.params?.default || 0,
|
|
|
|
|
message: "This value is required",
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
2021-07-26 05:50:46 +00:00
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
if (!Number.isFinite(value) && !isString(value)) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || 0,
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "number"`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-04-26 10:35:59 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
// check for min and max limits
|
|
|
|
|
let parsed: number = value as number;
|
|
|
|
|
if (isString(value)) {
|
|
|
|
|
if (/^\d+\.?\d*$/.test(value)) {
|
|
|
|
|
parsed = Number(value);
|
|
|
|
|
} else {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config.params?.default || 0,
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "number"`,
|
|
|
|
|
};
|
2021-03-04 18:58:43 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
}
|
2021-04-26 10:35:59 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (
|
|
|
|
|
config.params?.min !== undefined &&
|
|
|
|
|
Number.isFinite(config.params.min)
|
|
|
|
|
) {
|
|
|
|
|
if (parsed < Number(config.params.min)) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed,
|
|
|
|
|
message: `Minimum allowed value: ${config.params.min}`,
|
|
|
|
|
};
|
2021-04-26 10:35:59 +00:00
|
|
|
}
|
2021-03-04 18:58:43 +00:00
|
|
|
}
|
2021-04-26 10:35:59 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (
|
|
|
|
|
config.params?.max !== undefined &&
|
|
|
|
|
Number.isFinite(config.params.max)
|
|
|
|
|
) {
|
|
|
|
|
if (parsed > Number(config.params.max)) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed,
|
|
|
|
|
message: `Maximum allowed value: ${config.params.max}`,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (config.params?.natural && (parsed < 0 || !Number.isInteger(parsed))) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed,
|
|
|
|
|
message: `Value should be a positive integer`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-04-26 10:35:59 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed,
|
|
|
|
|
};
|
2021-01-04 10:16:08 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.BOOLEAN]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-03-24 22:05:04 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
if (value === undefined || value === null) {
|
|
|
|
|
if (config.params && config.params.required) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: !!config.params?.default,
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "boolean"`,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return { isValid: true, parsed: config.params?.default || value };
|
2021-03-24 22:05:04 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
const isABoolean = value === true || value === false;
|
|
|
|
|
const isStringTrueFalse = value === "true" || value === "false";
|
|
|
|
|
const isValid = isABoolean || isStringTrueFalse;
|
|
|
|
|
|
|
|
|
|
let parsed = value;
|
|
|
|
|
if (isStringTrueFalse) parsed = value !== "false";
|
2021-04-26 10:35:59 +00:00
|
|
|
|
2021-03-24 22:05:04 +00:00
|
|
|
if (!isValid) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || false,
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} "boolean"`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
|
2021-01-04 10:16:08 +00:00
|
|
|
return { isValid, parsed };
|
|
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.OBJECT]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-01-04 10:16:08 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
if (
|
|
|
|
|
value === undefined ||
|
|
|
|
|
value === null ||
|
|
|
|
|
(isString(value) && value.trim().length === 0)
|
|
|
|
|
) {
|
|
|
|
|
if (config.params && config.params.required) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || {},
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-03-24 12:12:24 +00:00
|
|
|
return {
|
|
|
|
|
isValid: true,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || value,
|
2021-03-24 12:12:24 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
|
|
|
|
|
if (isPlainObject(value)) {
|
|
|
|
|
return validatePlainObject(
|
|
|
|
|
config,
|
|
|
|
|
value as Record<string, unknown>,
|
|
|
|
|
props,
|
|
|
|
|
);
|
2021-01-04 10:16:08 +00:00
|
|
|
}
|
2021-03-30 09:02:25 +00:00
|
|
|
|
|
|
|
|
try {
|
2021-07-26 05:50:46 +00:00
|
|
|
const result = { parsed: JSON.parse(value as string), isValid: true };
|
|
|
|
|
if (isPlainObject(result.parsed)) {
|
|
|
|
|
return validatePlainObject(config, result.parsed, props);
|
2021-03-30 09:02:25 +00:00
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || {},
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
2021-03-30 09:02:25 +00:00
|
|
|
} catch (e) {
|
2021-02-02 14:42:49 +00:00
|
|
|
return {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || {},
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
2021-02-02 14:42:49 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.ARRAY]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-02-02 14:42:49 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
const invalidResponse = {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config.params?.default || [],
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} Array`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
2021-07-26 05:50:46 +00:00
|
|
|
if (value === undefined || value === null) {
|
|
|
|
|
if (config.params && config.params.required) {
|
|
|
|
|
invalidResponse.message =
|
|
|
|
|
"This property is required for the widget to function correctly";
|
|
|
|
|
return invalidResponse;
|
|
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
|
|
|
|
isValid: true,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: value,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (isString(value)) {
|
|
|
|
|
try {
|
|
|
|
|
const _value = JSON.parse(value);
|
|
|
|
|
if (Array.isArray(_value)) {
|
|
|
|
|
const result = validateArray(config, _value, props);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return invalidResponse;
|
|
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
return validateArray(config, value, props);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return invalidResponse;
|
2021-01-04 10:16:08 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.OBJECT_ARRAY]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-01-04 10:16:08 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
const invalidResponse = {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config.params?.default || [{}],
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR} Array of objects`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
2021-07-26 05:50:46 +00:00
|
|
|
if (
|
|
|
|
|
value === undefined ||
|
|
|
|
|
value === null ||
|
|
|
|
|
(!isString(value) && !Array.isArray(value))
|
|
|
|
|
) {
|
|
|
|
|
if (config.params?.required) return invalidResponse;
|
|
|
|
|
return { isValid: true, parsed: value };
|
2021-01-04 10:16:08 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
let parsed = value;
|
2021-01-04 10:16:08 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (isString(value)) {
|
|
|
|
|
try {
|
|
|
|
|
parsed = JSON.parse(value);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return invalidResponse;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-24 19:25:38 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (Array.isArray(parsed)) {
|
|
|
|
|
if (parsed.length === 0) return invalidResponse;
|
|
|
|
|
parsed.forEach((entry, index) => {
|
|
|
|
|
if (!isPlainObject(entry)) {
|
2021-01-04 10:16:08 +00:00
|
|
|
return {
|
2021-07-26 05:50:46 +00:00
|
|
|
...invalidResponse,
|
|
|
|
|
message: `Invalid object at index ${index}`,
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
});
|
|
|
|
|
return { isValid: true, parsed };
|
2021-02-16 10:29:08 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
return invalidResponse;
|
2021-02-16 10:29:08 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.DATE_ISO_STRING]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
|
|
|
|
): ValidationResponse => {
|
2021-01-19 07:29:15 +00:00
|
|
|
const invalidResponse = {
|
|
|
|
|
isValid: false,
|
2021-07-26 05:50:46 +00:00
|
|
|
parsed: config.params?.default || moment().toISOString(true),
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR}: ISO 8601 date string`,
|
2021-01-19 07:29:15 +00:00
|
|
|
};
|
2021-07-26 05:50:46 +00:00
|
|
|
if (value === undefined || value === null || !isString(value)) {
|
|
|
|
|
if (!config.params?.required) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
|
|
|
|
};
|
2021-01-19 07:29:15 +00:00
|
|
|
}
|
|
|
|
|
return invalidResponse;
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
if (isString(value)) {
|
|
|
|
|
if (!moment(value).isValid()) return invalidResponse;
|
2021-01-19 07:29:15 +00:00
|
|
|
|
2021-07-26 05:50:46 +00:00
|
|
|
if (
|
|
|
|
|
value === moment(value).toISOString() ||
|
|
|
|
|
value === moment(value).toISOString(true)
|
|
|
|
|
) {
|
2021-04-30 07:00:13 +00:00
|
|
|
return {
|
2021-07-26 05:50:46 +00:00
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
2021-04-30 07:00:13 +00:00
|
|
|
};
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
if (moment(value).isValid())
|
|
|
|
|
return { isValid: true, parsed: moment(value).toISOString(true) };
|
2021-04-30 07:00:13 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
return invalidResponse;
|
2021-04-30 07:00:13 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.FUNCTION]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-03-24 19:25:38 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
const invalidResponse = {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: undefined,
|
|
|
|
|
message: "Failed to validate",
|
|
|
|
|
};
|
|
|
|
|
if (config.params?.fnString && isString(config.params?.fnString)) {
|
2021-03-24 19:25:38 +00:00
|
|
|
try {
|
2021-07-26 05:50:46 +00:00
|
|
|
const { result } = evaluate(config.params.fnString, {}, [
|
|
|
|
|
value,
|
|
|
|
|
props,
|
|
|
|
|
_,
|
|
|
|
|
moment,
|
|
|
|
|
]);
|
|
|
|
|
return result;
|
2021-03-24 19:25:38 +00:00
|
|
|
} catch (e) {
|
2021-07-26 05:50:46 +00:00
|
|
|
console.error("Validation function error: ", { e });
|
2021-03-24 19:25:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
return invalidResponse;
|
2021-07-02 09:55:50 +00:00
|
|
|
},
|
2021-07-26 05:50:46 +00:00
|
|
|
[ValidationTypes.IMAGE_URL]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
props: Record<string, unknown>,
|
2021-07-02 09:55:50 +00:00
|
|
|
): ValidationResponse => {
|
2021-07-26 05:50:46 +00:00
|
|
|
const invalidResponse = {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config.params?.default || "",
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR}: base64 string or data uri or URL`,
|
|
|
|
|
};
|
|
|
|
|
const base64ImageRegex = /^data:image\/.*;base64/;
|
|
|
|
|
const imageUrlRegex = /(http(s?):)([/|.|\w|\s|-])*\.(?:jpeg|jpg|gif|png)??(?:&?[^=&]*=[^=&]*)*/;
|
|
|
|
|
if (
|
|
|
|
|
value === undefined ||
|
|
|
|
|
value === null ||
|
|
|
|
|
(isString(value) && value.trim().length === 0)
|
|
|
|
|
) {
|
|
|
|
|
if (config.params && config.params.required) return invalidResponse;
|
|
|
|
|
return { isValid: true, parsed: value };
|
2021-07-02 09:55:50 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
if (isString(value)) {
|
|
|
|
|
if (imageUrlRegex.test(value.trim())) {
|
|
|
|
|
return { isValid: true, parsed: value.trim() };
|
|
|
|
|
}
|
|
|
|
|
if (base64ImageRegex.test(value)) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if (btoa(atob(value)) === value) {
|
|
|
|
|
return { isValid: true, parsed: `data:image/png;base64,${value}` };
|
|
|
|
|
}
|
2021-07-02 07:09:17 +00:00
|
|
|
}
|
2021-07-26 05:50:46 +00:00
|
|
|
return invalidResponse;
|
2021-07-02 07:09:17 +00:00
|
|
|
},
|
2021-07-28 06:01:09 +00:00
|
|
|
[ValidationTypes.SAFE_URL]: (
|
|
|
|
|
config: ValidationConfig,
|
|
|
|
|
value: unknown,
|
|
|
|
|
): ValidationResponse => {
|
|
|
|
|
const invalidResponse = {
|
|
|
|
|
isValid: false,
|
|
|
|
|
parsed: config?.params?.default || "",
|
|
|
|
|
message: `${WIDGET_TYPE_VALIDATION_ERROR}: URL`,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
typeof value === "string" &&
|
|
|
|
|
(value.match(SAFE_URL_PATTERN) || value.match(DATA_URL_PATTERN))
|
|
|
|
|
) {
|
|
|
|
|
return {
|
|
|
|
|
isValid: true,
|
|
|
|
|
parsed: value,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
return invalidResponse;
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-01-04 10:16:08 +00:00
|
|
|
};
|