import { validateArray, validateBoolean, validateChain, validateChoice, validateKeyValueDictionary, validateMaybeTrimmedString, validateMissing, validateNonEmptyTrimmedString, validateNumber, validateOption, validateSetValue, validateString, } from "@biryani/core" import * as d3ScaleChromatic from "d3-scale-chromatic" import { ChartNodeType } from "../charts" import { validateIndicatorDimension } from "./indicators" const colorInterpolatorsName = Object.keys(d3ScaleChromatic) .filter(name => name.startsWith("interpolate")) .map(name => name.substring("interpolate".length)) function validateChartBasemap(data: any): [any, any] { if (data === null || data === undefined) { return [data, "Missing value"] } if (typeof data !== "object") { return [data, `Expected an object, got ${typeof data}`] } data = { ...data, } const remainingKeys = new Set(Object.keys(data)) const errors: { [key: string]: any } = {} for (const key of ["path", "topoJsonField"]) { remainingKeys.delete(key) const [value, error] = validateNonEmptyTrimmedString(data[key]) data[key] = value if (error !== null) { errors[key] = error } } for (const key of remainingKeys) { errors[key] = "Unexpected entry" } return [data, Object.keys(errors).length === 0 ? null : errors] } export function validateChartData(data: any): [any, any] { if (data === null || data === undefined) { return [data, "Missing value"] } if (typeof data !== "object") { return [data, `Expected an object, got ${typeof data}`] } data = { ...data, } const remainingKeys = new Set(Object.keys(data)) const errors: { [key: string]: any } = {} { const key = "basemap" remainingKeys.delete(key) const [value, error] = validateChartBasemap(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "breaks" remainingKeys.delete(key) const [value, error] = validateArray(validateNumber)(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "colorInterpolatorName" remainingKeys.delete(key) const [value, error] = validateChain( validateString, validateChoice(colorInterpolatorsName), )(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "colors" remainingKeys.delete(key) const [value, error] = validateArray(validateNonEmptyTrimmedString)( data[key], ) data[key] = value if (error !== null) { errors[key] = error } } { const key = "discretizationMethod" remainingKeys.delete(key) const [value, error] = validateChain( validateString, validateChoice([ "arithmetic_progression", "ck_means", "equal_interval", "quantiles", "standard_deviation", ]), )(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "indicator" remainingKeys.delete(key) const [value, error] = validateChartIndicator(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "labels" remainingKeys.delete(key) const [value, error] = validateKeyValueDictionary( validateString, validateString, )(data[key]) data[key] = value if (error !== null) { errors[key] = error } } for (const key of ["maxValue", "minValue"]) { remainingKeys.delete(key) const [value, error] = validateNumber(data[key]) data[key] = value if (error !== null) { errors[key] = error } } if ( errors.maxValue === undefined && errors.minValue === undefined && data.maxValue < data.minValue ) { errors.maxValue = "Max value must be greater than or equal to min value" errors.minValue = "Min value must be less than or equal to max value" } { const key = "reverseColors" remainingKeys.delete(key) const [value, error] = validateOption( [validateMissing, validateSetValue(false)], validateBoolean, )(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "values" remainingKeys.delete(key) const [value, error] = validateKeyValueDictionary( validateString, validateNumber, )(data[key]) data[key] = value if (error !== null) { errors[key] = error } } for (const key of remainingKeys) { errors[key] = "Unexpected entry" } return [data, Object.keys(errors).length === 0 ? null : errors] } function validateChartIndicator(data: any): [any, any] { if (data === null || data === undefined) { return [data, "Missing value"] } if (typeof data !== "object") { return [data, `Expected an object, got ${typeof data}`] } data = { ...data, } const remainingKeys = new Set(Object.keys(data)) const errors: { [key: string]: any } = {} { const key = "dimension" remainingKeys.delete(key) const [value, error] = validateIndicatorDimension(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "path" remainingKeys.delete(key) const [value, error] = validateNonEmptyTrimmedString(data[key]) data[key] = value if (error !== null) { errors[key] = error } } for (const key of remainingKeys) { errors[key] = "Unexpected entry" } return [data, Object.keys(errors).length === 0 ? null : errors] } export function validateChartNode(data: any): [any, any] { if (data === null || data === undefined) { return [data, "Missing value"] } if (typeof data !== "object") { return [data, `Expected an object, got ${typeof data}`] } data = { ...data, } const remainingKeys = new Set(Object.keys(data)) const errors: { [key: string]: any } = {} { const key = "description" remainingKeys.delete(key) const [value, error] = validateMaybeTrimmedString(data[key]) if (value === null) { delete data[key] } else { data[key] = value } if (error !== null) { errors[key] = error } } { const key = "name" remainingKeys.delete(key) const [value, error] = validateNonEmptyTrimmedString(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "path" remainingKeys.delete(key) const [value, error] = validateNonEmptyTrimmedString(data[key]) data[key] = value if (error !== null) { errors[key] = error } } { const key = "type" remainingKeys.delete(key) const [value, error] = validateChoice([ ChartNodeType.Chart, ChartNodeType.Group, ])(data[key]) data[key] = value if (error !== null) { errors[key] = error } } if (errors.type === undefined && data.type === ChartNodeType.Chart) { { const key = "data" remainingKeys.delete(key) const [value, error] = validateOption( validateMissing, validateChartData, )(data[key]) if (value === null) { delete data[key] } else { data[key] = value } if (error !== null) { errors[key] = error } } } for (const key of remainingKeys) { errors[key] = "Unexpected entry" } return [data, Object.keys(errors).length === 0 ? null : errors] }