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