import isNil from 'lodash/isNil'; import isPlainObject from 'lodash/isPlainObject'; import isString from 'lodash/isString'; import isUndefined from 'lodash/isUndefined'; import { EmbedErrorCodes, EmbedEvent, ErrorDetailsTypes, HostEvent } from '../../types'; import { ERROR_MESSAGE } from '../../errors'; import { ApplicabilityLevel } from '../../contracts/ui-passthrough-contracts'; import { HostEventRequest } from '../../contracts/host-event-contracts'; import { embedEventStatus } from '../../utils'; const isValidApplicability = (a?: { level?: string; targetId?: string }) => { if (isUndefined(a)) return true; // targetId is not required at LIVEBOARD level, since the filter applies to the whole Liveboard return isPlainObject(a) && Object.values(ApplicabilityLevel).includes(a.level as ApplicabilityLevel) && (a.level === ApplicabilityLevel.Liveboard || (isString(a.targetId) && a.targetId.trim().length > 0)); }; export function isValidUpdateFiltersPayload( payload: HostEventRequest | undefined, ): boolean { if (!payload) return false; const isValidFilter = (f: { column?: string; oper?: string; values?: unknown[]; type?: string; columnName?: string; operator?: string; applicability?: { level?: string; targetId?: string }; }) => { const hasColumn = typeof f.column === 'string' || typeof f.columnName === 'string'; const hasOperator = typeof f.oper === 'string' || typeof f.operator === 'string'; const hasValues = Array.isArray(f.values); const validType = !f.type || typeof f.type === 'string'; return hasColumn && hasOperator && hasValues && validType && isValidApplicability(f.applicability); }; const hasValidFilter = payload.filter && isValidFilter(payload.filter); const hasValidFilters = Array.isArray(payload.filters) && payload.filters.length > 0 && payload.filters.every(isValidFilter); return !!(hasValidFilter || hasValidFilters); } export function isValidUpdateParametersPayload(payload: unknown): boolean { // Only validates the applicability of each parameter (null treated as absent); the rest is forwarded as-is for backward compatibility. if (!Array.isArray(payload)) return true; return payload.every((p) => { if (!isPlainObject(p)) return true; const { applicability } = p as { applicability?: { level?: string; targetId?: string } }; return isNil(applicability) || isValidApplicability(applicability); }); } export function isValidDrillDownPayload( payload: HostEventRequest | undefined, ): boolean { if (!payload) return false; const points = payload.points; if (!points || typeof points !== 'object') return false; const hasClickedPoint = 'clickedPoint' in points && points.clickedPoint != null; const hasSelectedPoints = Array.isArray(points.selectedPoints) && points.selectedPoints.length > 0; return hasClickedPoint || hasSelectedPoints; } export type ValidationError = Error & { isValidationError?: boolean; embedErrorDetails?: { type: EmbedEvent.Error; data: { errorType: ErrorDetailsTypes; message: string; code: EmbedErrorCodes; error: string }; status: typeof embedEventStatus.END }; }; export function createValidationError(message: string): never { const err = new Error(message) as ValidationError; err.isValidationError = true; err.embedErrorDetails = { type: EmbedEvent.Error, data:{ errorType: ErrorDetailsTypes.VALIDATION_ERROR, message, code: EmbedErrorCodes.HOST_EVENT_VALIDATION, error: message }, status:embedEventStatus.END }; throw err; } export function throwUpdateFiltersValidationError(): never { createValidationError(ERROR_MESSAGE.UPDATEFILTERS_INVALID_PAYLOAD); } export function throwDrillDownValidationError(): never { createValidationError(ERROR_MESSAGE.DRILLDOWN_INVALID_PAYLOAD); } export function throwUpdateParametersValidationError(): never { createValidationError(ERROR_MESSAGE.UPDATEPARAMETERS_INVALID_PAYLOAD); }