or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-models.mddashboard.mddata-connection.mddata-formatting.mdindex.mdplugin-system.mdtranslation.mdui-styling.mdvalidation-math.md

validation-math.mddocs/

0

# Validation & Math

1

2

This module provides input validation utilities and mathematical expression evaluation capabilities for Superset applications. It includes both modern and legacy validation functions for common data types, as well as a powerful expression evaluator that supports mathematical operations, logical comparisons, and variable substitution.

3

4

## Overview

5

6

The validation and math module serves two primary purposes: ensuring data integrity through comprehensive input validation and providing dynamic mathematical computation through expression evaluation. The validation system supports both modern and legacy validation patterns, while the math expression system enables conditional logic and calculations with support for comparison operators and logical operations.

7

8

## Input Validation

9

10

### Modern Validation Functions { .api }

11

12

Current validation functions that return error messages or false for valid inputs:

13

14

```typescript

15

import {

16

validateNumber,

17

validateInteger,

18

validateNonEmpty

19

} from '@superset-ui/core';

20

21

// Validate numeric input

22

function validateNumber(value: unknown): string | false;

23

24

// Validate integer input

25

function validateInteger(value: unknown): string | false;

26

27

// Validate non-empty input (not null, undefined, empty string, etc.)

28

function validateNonEmpty(value: unknown): string | false;

29

```

30

31

### Legacy Validation Functions { .api }

32

33

Legacy validation functions for backward compatibility:

34

35

```typescript

36

import {

37

legacyValidateNumber,

38

legacyValidateInteger

39

} from '@superset-ui/core';

40

41

// Legacy number validation

42

function legacyValidateNumber(value: unknown): string | false;

43

44

// Legacy integer validation

45

function legacyValidateInteger(value: unknown): string | false;

46

```

47

48

## Mathematical Expressions

49

50

### Expression Evaluation { .api }

51

52

Powerful mathematical expression evaluator with support for variables and logical operations:

53

54

```typescript

55

import { evalExpression, isValidExpression } from '@superset-ui/core';

56

57

// Evaluate mathematical expression with variable substitution

58

function evalExpression(expression: string, value: number): number;

59

60

// Validate expression syntax

61

function isValidExpression(expression: string): boolean;

62

```

63

64

### Supported Operations

65

66

The expression evaluator supports a comprehensive set of mathematical and logical operations:

67

68

#### Arithmetic Operations

69

- `+` - Addition

70

- `-` - Subtraction

71

- `*` - Multiplication

72

- `/` - Division

73

- `%` - Modulo

74

- `^` - Exponentiation

75

76

#### Comparison Operations

77

- `==` - Equal to

78

- `>` - Greater than

79

- `>=` - Greater than or equal to

80

- `<` - Less than

81

- `<=` - Less than or equal to

82

83

#### Logical Operations

84

- `and` - Logical AND

85

- `or` - Logical OR

86

- `xor` - Exclusive OR (XOR)

87

- `&` - Bitwise AND

88

- `|` - Bitwise OR

89

90

#### Variables

91

- `x` - Variable that gets substituted with the provided value

92

93

## Usage Examples

94

95

### Basic Input Validation

96

97

```typescript

98

import {

99

validateNumber,

100

validateInteger,

101

validateNonEmpty

102

} from '@superset-ui/core';

103

104

// Number validation

105

const numberValidation1 = validateNumber(42); // false (valid)

106

const numberValidation2 = validateNumber('123.45'); // false (valid)

107

const numberValidation3 = validateNumber('abc'); // "is expected to be a number"

108

const numberValidation4 = validateNumber(null); // "is expected to be a number"

109

110

// Integer validation

111

const integerValidation1 = validateInteger(42); // false (valid)

112

const integerValidation2 = validateInteger('123'); // false (valid)

113

const integerValidation3 = validateInteger('12.34'); // "is expected to be an integer"

114

const integerValidation4 = validateInteger('abc'); // "is expected to be an integer"

115

116

// Non-empty validation

117

const emptyValidation1 = validateNonEmpty('hello'); // false (valid)

118

const emptyValidation2 = validateNonEmpty(0); // false (valid - 0 is not empty)

119

const emptyValidation3 = validateNonEmpty(''); // "cannot be empty"

120

const emptyValidation4 = validateNonEmpty(null); // "cannot be empty"

121

const emptyValidation5 = validateNonEmpty(undefined); // "cannot be empty"

122

```

123

124

### Form Validation Integration

125

126

```typescript

127

import React, { useState } from 'react';

128

import { validateNumber, validateInteger } from '@superset-ui/core';

129

130

interface FormData {

131

rowLimit: string;

132

threshold: string;

133

}

134

135

const ChartConfigForm: React.FC = () => {

136

const [formData, setFormData] = useState<FormData>({

137

rowLimit: '',

138

threshold: ''

139

});

140

141

const [errors, setErrors] = useState<Partial<FormData>>({});

142

143

const validateForm = (data: FormData) => {

144

const newErrors: Partial<FormData> = {};

145

146

// Validate row limit as integer

147

const rowLimitError = validateInteger(data.rowLimit);

148

if (rowLimitError) {

149

newErrors.rowLimit = rowLimitError;

150

}

151

152

// Validate threshold as number

153

const thresholdError = validateNumber(data.threshold);

154

if (thresholdError) {

155

newErrors.threshold = thresholdError;

156

}

157

158

setErrors(newErrors);

159

return Object.keys(newErrors).length === 0;

160

};

161

162

const handleSubmit = (e: React.FormEvent) => {

163

e.preventDefault();

164

if (validateForm(formData)) {

165

console.log('Form is valid:', formData);

166

}

167

};

168

169

return (

170

<form onSubmit={handleSubmit}>

171

<div>

172

<label>Row Limit (Integer):</label>

173

<input

174

value={formData.rowLimit}

175

onChange={(e) => setFormData({...formData, rowLimit: e.target.value})}

176

/>

177

{errors.rowLimit && <span className="error">{errors.rowLimit}</span>}

178

</div>

179

180

<div>

181

<label>Threshold (Number):</label>

182

<input

183

value={formData.threshold}

184

onChange={(e) => setFormData({...formData, threshold: e.target.value})}

185

/>

186

{errors.threshold && <span className="error">{errors.threshold}</span>}

187

</div>

188

189

<button type="submit">Submit</button>

190

</form>

191

);

192

};

193

```

194

195

### Mathematical Expression Evaluation

196

197

```typescript

198

import { evalExpression, isValidExpression } from '@superset-ui/core';

199

200

// Basic arithmetic

201

const result1 = evalExpression('x + 10', 5); // 15

202

const result2 = evalExpression('x * 2 + 1', 3); // 7

203

const result3 = evalExpression('x ^ 2', 4); // 16

204

const result4 = evalExpression('x % 3', 10); // 1

205

206

// Comparison operations (return 1 for true, 0 for false)

207

const comparison1 = evalExpression('x > 10', 15); // 1 (true)

208

const comparison2 = evalExpression('x == 5', 5); // 1 (true)

209

const comparison3 = evalExpression('x < 0', 5); // 0 (false)

210

const comparison4 = evalExpression('x >= 100', 50); // 0 (false)

211

const comparison5 = evalExpression('x <= 10', 10); // 1 (true)

212

213

// Logical operations

214

const logical1 = evalExpression('x > 5 and x < 15', 10); // 1 (true)

215

const logical2 = evalExpression('x < 0 or x > 100', 5); // 0 (false)

216

const logical3 = evalExpression('x == 5 xor x == 10', 5); // 1 (true - only first condition met)

217

218

// Complex expressions

219

const complex1 = evalExpression('(x * 2) > 20 and (x + 5) < 30', 12); // 1 (true)

220

const complex2 = evalExpression('x >= 0 and x <= 100', 50); // 1 (true)

221

```

222

223

### Conditional Logic for Data Processing

224

225

```typescript

226

import { evalExpression, isValidExpression } from '@superset-ui/core';

227

228

// Create data filters based on expressions

229

interface DataFilter {

230

expression: string;

231

description: string;

232

}

233

234

const dataFilters: DataFilter[] = [

235

{ expression: 'x > 0', description: 'Positive values only' },

236

{ expression: 'x >= 18 and x <= 65', description: 'Working age (18-65)' },

237

{ expression: 'x % 2 == 0', description: 'Even numbers only' },

238

{ expression: 'x > 1000 or x < -1000', description: 'Outliers (absolute value > 1000)' }

239

];

240

241

// Apply filters to dataset

242

const applyFilters = (data: number[], filters: DataFilter[]): number[] => {

243

return data.filter(value => {

244

return filters.every(filter => {

245

if (!isValidExpression(filter.expression)) {

246

console.warn(`Invalid expression: ${filter.expression}`);

247

return true; // Include value if expression is invalid

248

}

249

250

return evalExpression(filter.expression, value) === 1;

251

});

252

});

253

};

254

255

// Usage

256

const dataset = [-500, 25, 42, 1500, -2000, 30, 17, 66];

257

const activeFilters = [

258

dataFilters[0], // Positive values only

259

dataFilters[1] // Working age (18-65)

260

];

261

262

const filteredData = applyFilters(dataset, activeFilters);

263

console.log(filteredData); // [25, 42, 30] (positive and between 18-65)

264

```

265

266

### Chart Data Thresholds

267

268

```typescript

269

import { evalExpression, isValidExpression } from '@superset-ui/core';

270

271

// Define threshold configurations for charts

272

interface ThresholdConfig {

273

condition: string;

274

color: string;

275

label: string;

276

}

277

278

const thresholds: ThresholdConfig[] = [

279

{ condition: 'x < 25', color: '#FF4444', label: 'Critical' },

280

{ condition: 'x >= 25 and x < 75', color: '#FFA500', label: 'Warning' },

281

{ condition: 'x >= 75', color: '#00AA00', label: 'Good' }

282

];

283

284

// Apply threshold coloring to chart data

285

const applyThresholdColoring = (values: number[], thresholds: ThresholdConfig[]) => {

286

return values.map(value => {

287

// Find first matching threshold

288

const threshold = thresholds.find(t => {

289

if (!isValidExpression(t.condition)) return false;

290

return evalExpression(t.condition, value) === 1;

291

});

292

293

return {

294

value,

295

color: threshold?.color || '#666666',

296

label: threshold?.label || 'Unknown',

297

status: threshold ? 'matched' : 'no_match'

298

};

299

});

300

};

301

302

// Usage

303

const performanceScores = [15, 45, 85, 92, 30, 60];

304

const coloredData = applyThresholdColoring(performanceScores, thresholds);

305

306

coloredData.forEach(item => {

307

console.log(`Score: ${item.value}, Status: ${item.label}, Color: ${item.color}`);

308

});

309

// Score: 15, Status: Critical, Color: #FF4444

310

// Score: 45, Status: Warning, Color: #FFA500

311

// Score: 85, Status: Good, Color: #00AA00

312

// etc.

313

```

314

315

### Dynamic Alert Rules

316

317

```typescript

318

import { evalExpression, isValidExpression } from '@superset-ui/core';

319

320

// Alert rule configuration

321

interface AlertRule {

322

id: string;

323

name: string;

324

expression: string;

325

message: string;

326

severity: 'low' | 'medium' | 'high' | 'critical';

327

}

328

329

const alertRules: AlertRule[] = [

330

{

331

id: 'high_cpu',

332

name: 'High CPU Usage',

333

expression: 'x > 80',

334

message: 'CPU usage is above 80%',

335

severity: 'high'

336

},

337

{

338

id: 'memory_critical',

339

name: 'Critical Memory Usage',

340

expression: 'x >= 95',

341

message: 'Memory usage is critically high',

342

severity: 'critical'

343

},

344

{

345

id: 'disk_warning',

346

name: 'Disk Space Warning',

347

expression: 'x > 70 and x <= 90',

348

message: 'Disk space is running low',

349

severity: 'medium'

350

}

351

];

352

353

// Evaluate alerts for system metrics

354

interface SystemMetric {

355

name: string;

356

value: number;

357

timestamp: Date;

358

}

359

360

interface Alert {

361

ruleId: string;

362

ruleName: string;

363

metric: SystemMetric;

364

message: string;

365

severity: string;

366

triggered: boolean;

367

}

368

369

const evaluateAlerts = (metrics: SystemMetric[], rules: AlertRule[]): Alert[] => {

370

const alerts: Alert[] = [];

371

372

metrics.forEach(metric => {

373

rules.forEach(rule => {

374

if (!isValidExpression(rule.expression)) {

375

console.error(`Invalid alert expression for rule ${rule.id}: ${rule.expression}`);

376

return;

377

}

378

379

const triggered = evalExpression(rule.expression, metric.value) === 1;

380

381

alerts.push({

382

ruleId: rule.id,

383

ruleName: rule.name,

384

metric,

385

message: rule.message,

386

severity: rule.severity,

387

triggered

388

});

389

});

390

});

391

392

return alerts.filter(alert => alert.triggered);

393

};

394

395

// Usage

396

const systemMetrics: SystemMetric[] = [

397

{ name: 'CPU Usage', value: 85, timestamp: new Date() },

398

{ name: 'Memory Usage', value: 72, timestamp: new Date() },

399

{ name: 'Disk Usage', value: 88, timestamp: new Date() }

400

];

401

402

const activeAlerts = evaluateAlerts(systemMetrics, alertRules);

403

activeAlerts.forEach(alert => {

404

console.log(`[${alert.severity.toUpperCase()}] ${alert.ruleName}: ${alert.message} (Value: ${alert.metric.value})`);

405

});

406

```

407

408

### Expression Validation for User Input

409

410

```typescript

411

import { isValidExpression } from '@superset-ui/core';

412

413

// Validate user-entered expressions in real-time

414

const validateUserExpression = (expression: string): { valid: boolean; error?: string } => {

415

if (!expression.trim()) {

416

return { valid: false, error: 'Expression cannot be empty' };

417

}

418

419

if (!isValidExpression(expression)) {

420

return { valid: false, error: 'Invalid mathematical expression' };

421

}

422

423

// Additional validation rules

424

const forbiddenPatterns = [

425

/[a-zA-Z]+/g, // No letters except 'x', 'and', 'or', 'xor'

426

/\.\./g, // No double dots

427

/[{}[\]]/g // No brackets/braces

428

];

429

430

const allowedWords = ['x', 'and', 'or', 'xor'];

431

const letterMatches = expression.match(/[a-zA-Z]+/g);

432

433

if (letterMatches) {

434

const invalidWords = letterMatches.filter(word => !allowedWords.includes(word));

435

if (invalidWords.length > 0) {

436

return {

437

valid: false,

438

error: `Invalid words: ${invalidWords.join(', ')}. Only 'x', 'and', 'or', 'xor' are allowed.`

439

};

440

}

441

}

442

443

for (const pattern of forbiddenPatterns.slice(1)) { // Skip the letter pattern

444

if (pattern.test(expression)) {

445

return { valid: false, error: 'Expression contains forbidden characters' };

446

}

447

}

448

449

return { valid: true };

450

};

451

452

// React component for expression input

453

const ExpressionInput: React.FC<{

454

value: string;

455

onChange: (value: string) => void;

456

}> = ({ value, onChange }) => {

457

const validation = validateUserExpression(value);

458

459

return (

460

<div>

461

<input

462

value={value}

463

onChange={(e) => onChange(e.target.value)}

464

placeholder="Enter expression (e.g., x > 10 and x < 100)"

465

style={{

466

borderColor: validation.valid ? 'green' : 'red'

467

}}

468

/>

469

{!validation.valid && validation.error && (

470

<div style={{ color: 'red', fontSize: '12px' }}>

471

{validation.error}

472

</div>

473

)}

474

{validation.valid && (

475

<div style={{ color: 'green', fontSize: '12px' }}>

476

✓ Valid expression

477

</div>

478

)}

479

</div>

480

);

481

};

482

```

483

484

### Advanced Mathematical Operations

485

486

```typescript

487

import { evalExpression } from '@superset-ui/core';

488

489

// Mathematical utilities using expressions

490

const mathUtils = {

491

// Check if number is in range

492

isInRange: (value: number, min: number, max: number): boolean => {

493

return evalExpression(`x >= ${min} and x <= ${max}`, value) === 1;

494

},

495

496

// Check if number is even

497

isEven: (value: number): boolean => {

498

return evalExpression('x % 2 == 0', value) === 1;

499

},

500

501

// Check if number is prime (simplified check for small numbers)

502

isPrime: (value: number): boolean => {

503

if (value < 2) return false;

504

if (value === 2) return true;

505

if (value % 2 === 0) return false;

506

507

// Check odd divisors up to sqrt(value)

508

for (let i = 3; i <= Math.sqrt(value); i += 2) {

509

if (evalExpression(`x % ${i} == 0`, value) === 1) {

510

return false;

511

}

512

}

513

return true;

514

},

515

516

// Apply scaling based on conditions

517

conditionalScale: (value: number): number => {

518

// Scale differently based on value ranges

519

if (evalExpression('x < 10', value)) return value * 10;

520

if (evalExpression('x >= 10 and x < 100', value)) return value * 2;

521

if (evalExpression('x >= 100', value)) return value * 0.1;

522

return value;

523

}

524

};

525

526

// Usage

527

const numbers = [2, 5, 8, 15, 25, 150];

528

numbers.forEach(num => {

529

console.log(`${num}: even=${mathUtils.isEven(num)}, prime=${mathUtils.isPrime(num)}, scaled=${mathUtils.conditionalScale(num)}`);

530

});

531

```

532

533

## Related Documentation

534

535

- [Core Models & Utilities](./core-models.md) - Utility functions and type checking

536

- [Data Formatting](./data-formatting.md) - Number formatting and validation integration

537

- [Translation](./translation.md) - Internationalized error messages

538

- [Plugin System](./plugin-system.md) - Chart plugin validation and configuration