Application-wide constants used throughout the Lightdash platform.
This module provides the following functionality:
enum SessionStorageKeys {
SEND_NOW_SCHEDULER_FILTERS = 'sendNowSchedulerFilters',
SEND_NOW_SCHEDULER_PARAMETERS = 'sendNowSchedulerParameters',
}Enumeration of session storage keys used by the application for storing temporary state.
Example:
import { SessionStorageKeys } from '@lightdash/common';
// Store scheduler filters in session storage
sessionStorage.setItem(
SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS,
JSON.stringify(filters)
);
// Retrieve scheduler parameters
const params = sessionStorage.getItem(
SessionStorageKeys.SEND_NOW_SCHEDULER_PARAMETERS
);const MAX_SAFE_INTEGER: number; // Value: 2_147_483_647Maximum safe 32-bit signed integer used for SQL query result limits to avoid issues with certain warehouses like Redshift.
Example:
import { MAX_SAFE_INTEGER } from '@lightdash/common';
// Use for "all results" queries
const query = {
...metricQuery,
limit: MAX_SAFE_INTEGER // Fetch all results
};const MAX_METRICS_TREE_NODE_COUNT: number;Maximum number of nodes allowed in a metrics tree catalog.
Example:
import { MAX_METRICS_TREE_NODE_COUNT } from '@lightdash/common';
if (nodeCount > MAX_METRICS_TREE_NODE_COUNT) {
throw new Error(`Metrics tree exceeds maximum node count of ${MAX_METRICS_TREE_NODE_COUNT}`);
}import { SessionStorageKeys } from '@lightdash/common';
// Scheduler Filters Storage
interface SchedulerFilters {
dateRange: { start: Date; end: Date };
dashboards: string[];
users: string[];
}
function saveSchedulerFilters(filters: SchedulerFilters) {
sessionStorage.setItem(
SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS,
JSON.stringify(filters)
);
}
function loadSchedulerFilters(): SchedulerFilters | null {
const stored = sessionStorage.getItem(
SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS
);
if (!stored) {
return null;
}
try {
return JSON.parse(stored);
} catch (error) {
console.error('Failed to parse scheduler filters:', error);
return null;
}
}
function clearSchedulerFilters() {
sessionStorage.removeItem(SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS);
}import { SessionStorageKeys } from '@lightdash/common';
interface SchedulerParameters {
format: 'pdf' | 'png' | 'csv';
recipients: string[];
message?: string;
}
function saveSchedulerParameters(params: SchedulerParameters) {
sessionStorage.setItem(
SessionStorageKeys.SEND_NOW_SCHEDULER_PARAMETERS,
JSON.stringify(params)
);
}
function getSchedulerParameters(): SchedulerParameters | null {
const stored = sessionStorage.getItem(
SessionStorageKeys.SEND_NOW_SCHEDULER_PARAMETERS
);
return stored ? JSON.parse(stored) : null;
}
// Usage in scheduler UI
function SchedulerForm() {
const [params, setParams] = useState<SchedulerParameters>(() => {
// Load from session storage on mount
return getSchedulerParameters() || {
format: 'pdf',
recipients: [],
};
});
useEffect(() => {
// Save to session storage on change
saveSchedulerParameters(params);
}, [params]);
// ... form implementation
}import { MAX_SAFE_INTEGER } from '@lightdash/common';
// Fetch all results without limit
function fetchAllResults(metricQuery: MetricQuery) {
return executeQuery({
...metricQuery,
limit: MAX_SAFE_INTEGER,
});
}
// Apply safe limit
function applySafeLimit(requestedLimit: number): number {
if (requestedLimit === -1 || requestedLimit > MAX_SAFE_INTEGER) {
// Use max safe integer for "unlimited" requests
return MAX_SAFE_INTEGER;
}
return Math.min(requestedLimit, MAX_SAFE_INTEGER);
}
// SQL Runner query
async function runSqlQuery(sql: string, limit?: number) {
const safeLimit = limit ? applySafeLimit(limit) : 500;
const query = `
${sql}
LIMIT ${safeLimit}
`;
return await warehouse.execute(query);
}import { MAX_METRICS_TREE_NODE_COUNT } from '@lightdash/common';
interface MetricsTreeNode {
id: string;
name: string;
children: MetricsTreeNode[];
}
function countNodes(node: MetricsTreeNode): number {
let count = 1; // Count this node
for (const child of node.children) {
count += countNodes(child);
}
return count;
}
function validateMetricsTree(root: MetricsTreeNode): void {
const totalNodes = countNodes(root);
if (totalNodes > MAX_METRICS_TREE_NODE_COUNT) {
throw new Error(
`Metrics tree has ${totalNodes} nodes, exceeding the maximum of ${MAX_METRICS_TREE_NODE_COUNT}`
);
}
}
// Usage
try {
validateMetricsTree(metricsTreeRoot);
await saveMetricsTree(metricsTreeRoot);
} catch (error) {
console.error('Invalid metrics tree:', error.message);
}import { SessionStorageKeys } from '@lightdash/common';
// External dependency - Install separately: npm install react
import { useState, useEffect } from 'react';
function useSessionStorage<T>(
key: SessionStorageKeys,
initialValue: T
): [T, (value: T) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = sessionStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error('Error loading from session storage:', error);
return initialValue;
}
});
const setValue = (value: T) => {
try {
setStoredValue(value);
sessionStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Error saving to session storage:', error);
}
};
return [storedValue, setValue];
}
// Usage
function SchedulerComponent() {
const [filters, setFilters] = useSessionStorage(
SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS,
{ dashboards: [], users: [] }
);
return (
<div>
{/* Component implementation */}
</div>
);
}import { MAX_SAFE_INTEGER } from '@lightdash/common';
app.get('/api/sql-runner/query', async (req, res) => {
const { sql, limit } = req.body;
// Apply safe limit
const safeLimit = limit === undefined
? 500
: Math.min(limit, MAX_SAFE_INTEGER);
try {
const results = await warehouse.query(sql, { limit: safeLimit });
res.json({
results,
limit: safeLimit,
isLimitReached: results.rows.length >= safeLimit,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});import { SessionStorageKeys } from '@lightdash/common';
// Clear all scheduler-related session storage
function clearSchedulerSession() {
sessionStorage.removeItem(SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS);
sessionStorage.removeItem(SessionStorageKeys.SEND_NOW_SCHEDULER_PARAMETERS);
}
// Clear on logout
function handleLogout() {
clearSchedulerSession();
// ... other logout logic
}
// Clear on navigation away from scheduler
function useSchedulerCleanup() {
useEffect(() => {
return () => {
// Cleanup on unmount
clearSchedulerSession();
};
}, []);
}import {
SessionStorageKeys,
MAX_SAFE_INTEGER,
MAX_METRICS_TREE_NODE_COUNT,
} from '@lightdash/common';
describe('Constants', () => {
describe('SessionStorageKeys', () => {
it('should have correct filter key', () => {
expect(SessionStorageKeys.SEND_NOW_SCHEDULER_FILTERS).toBe(
'sendNowSchedulerFilters'
);
});
it('should have correct parameters key', () => {
expect(SessionStorageKeys.SEND_NOW_SCHEDULER_PARAMETERS).toBe(
'sendNowSchedulerParameters'
);
});
});
describe('MAX_SAFE_INTEGER', () => {
it('should be 32-bit signed integer max', () => {
expect(MAX_SAFE_INTEGER).toBe(2_147_483_647);
});
it('should handle query limits', () => {
const limit = applySafeLimit(Number.MAX_VALUE);
expect(limit).toBe(MAX_SAFE_INTEGER);
});
});
describe('MAX_METRICS_TREE_NODE_COUNT', () => {
it('should be defined', () => {
expect(MAX_METRICS_TREE_NODE_COUNT).toBeDefined();
expect(typeof MAX_METRICS_TREE_NODE_COUNT).toBe('number');
});
});
});