Skip to content

Conversation

rebenitez1802
Copy link
Contributor

SUMMARY

Implements enhanced theme validation and error handling for Superset's theme system with comprehensive fallback mechanisms. This PR introduces:

  • Enhanced theme token validation with detailed error reporting for invalid theme tokens
  • error recovery with fallback to system default themes when theme application fails
  • Feature flag controlled validation (ENHANCED_THEME_VALIDATION) to enable/disable advanced validation
  • Partial theme loading that filters out invalid tokens while preserving valid ones
  • Improved ThemeModal with real-time validation feedback and user guidance

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Theme_Token_validation

TESTING INSTRUCTIONS

To test live token validation:

  1. Enable enhanced validation:
    - Set ENHANCED_THEME_VALIDATION: True in feature flags

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

Copy link

korbit-ai bot commented Sep 30, 2025

Based on your review schedule, I'll hold off on reviewing this PR until it's marked as ready for review. If you'd like me to take a look now, comment /korbit-review.

Your admin can change your review schedule in the Korbit Console

Copy link

codecov bot commented Sep 30, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.86%. Comparing base (412587a) to head (007fa5d).
⚠️ Report is 8 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master   #35349       +/-   ##
===========================================
+ Coverage        0   71.86%   +71.86%     
===========================================
  Files           0      589      +589     
  Lines           0    43607    +43607     
  Branches        0     4719     +4719     
===========================================
+ Hits            0    31336    +31336     
- Misses          0    11032    +11032     
- Partials        0     1239     +1239     
Flag Coverage Δ
hive 46.27% <ø> (?)
mysql 70.89% <ø> (?)
postgres 70.94% <ø> (?)
presto 49.97% <ø> (?)
python 71.82% <ø> (?)
sqlite 70.54% <ø> (?)
unit 100.00% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rebenitez1802 rebenitez1802 marked this pull request as ready for review October 1, 2025 01:56
Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review by Korbit AI

Korbit automatically attempts to detect when you fix issues in new commits.
Category Issue Status
Functionality Enhanced theme validation disabled by default ▹ view 🧠 Not in standard
Design Repeated Default Object Structure ▹ view 🧠 Not in standard
Design Duplicated Feature Flag Logic ▹ view 🧠 Not in standard
Functionality Validation results lost on JSON errors ▹ view 🧠 Not in scope
Performance Inefficient Set recreation in color validation ▹ view 🧠 Incorrect
Performance Sequential API calls in theme fallback logic ▹ view
Performance Unnecessary validation hook execution ▹ view 🧠 Incorrect
Performance Repeated JSON parsing without memoization ▹ view 🧠 Incorrect
Performance Inefficient regex pattern creation ▹ view 🧠 Not in standard
Design Validation Logic Not Separated from State Management ▹ view 🧠 Not in standard
Files scanned
File Path Reviewed
superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
superset-frontend/src/theme/hooks/useThemeValidation.ts
superset-frontend/src/features/themes/ThemeModal.tsx
superset-frontend/src/theme/utils/themeTokenValidation.ts
superset-frontend/src/theme/ThemeController.ts
superset/config.py

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

"EMBEDDABLE_CHARTS": True,
# Enhanced theme validation in theme editor with real-time token validation
# and partial theme loading for invalid tokens
"ENHANCED_THEME_VALIDATION": False,

This comment was marked as resolved.

Comment on lines +241 to +243
const isEmptyTheme = (jsonData?: string) => {
if (!jsonData?.trim()) return true;
try {
const parsed = JSON.parse(jsonData);
// Check if it's an empty object or array
return Object.keys(parsed).length === 0;
} catch (e) {
return false; // If it's not valid JSON, let other validation handle it
}
};

This comment was marked as resolved.

Comment on lines +124 to +139
const themeValidation = useThemeValidation(currentTheme?.json_data || '', {
enabled: !isReadOnly && Boolean(currentTheme?.json_data),
themeName: currentTheme?.theme_name || 'Unknown Theme',
});

This comment was marked as resolved.

Comment on lines 252 to 339
const validate = () => {
if (isReadOnly) {
if (isReadOnly || !currentTheme) {
setDisableSave(true);
return;
}

if (
currentTheme?.theme_name.length &&
currentTheme?.json_data?.length &&
isValidJson(currentTheme.json_data)
) {
setDisableSave(false);
const hasValidName = Boolean(currentTheme?.theme_name?.length);
const hasValidJsonData = Boolean(currentTheme?.json_data?.length);
const isValidJsonSyntax = isValidJson(currentTheme?.json_data);
const isNotEmpty = !isEmptyTheme(currentTheme?.json_data);

// Basic validation requirements
const basicValidation =
hasValidName && hasValidJsonData && isValidJsonSyntax && isNotEmpty;

if (isEnhancedValidationEnabled && currentTheme) {
// Enhanced validation: allow saving even with token warnings, but block on JSON syntax errors
const enhancedValidation = basicValidation && !themeValidation.hasErrors;
setDisableSave(!enhancedValidation);
} else {
setDisableSave(true);
// Original validation logic
setDisableSave(!basicValidation);
}
};

This comment was marked as resolved.

Comment on lines +129 to +159
const enhancedValidation = useMemo(() => {
// Skip if basic validation is disabled or JSON has syntax errors
if (!enabled || jsonAnnotations.length > 0 || !debouncedJsonValue?.trim()) {
return {
annotations: [],
validTokenCount: 0,
invalidTokenCount: 0,
errorMessages: [],
};
}

// Only run enhanced validation if feature flag is enabled
try {
const isEnabled = isFeatureEnabled(FeatureFlag.EnhancedThemeValidation);
if (!isEnabled) {
return {
annotations: [],
validTokenCount: 0,
invalidTokenCount: 0,
errorMessages: [],
};
}
} catch (error) {
// Feature flag check failed - assume disabled
return {
annotations: [],
validTokenCount: 0,
invalidTokenCount: 0,
errorMessages: [],
};
}

This comment was marked as resolved.

Comment on lines +242 to +251
export function useIsEnhancedValidationEnabled(): boolean {
return useMemo(() => {
try {
return isFeatureEnabled(FeatureFlag.EnhancedThemeValidation);
} catch (error) {
// Feature flag check failed - assume disabled
return false;
}
}, []);
}

This comment was marked as resolved.

Comment on lines +131 to +138
if (!enabled || jsonAnnotations.length > 0 || !debouncedJsonValue?.trim()) {
return {
annotations: [],
validTokenCount: 0,
invalidTokenCount: 0,
errorMessages: [],
};
}

This comment was marked as resolved.

Comment on lines +48 to +50
const propertyPattern = new RegExp(
`"${tokenName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}"\\s*:`,
);

This comment was marked as resolved.

Comment on lines +377 to +409
const namedColors = new Set([
'transparent',
'inherit',
'currentColor',
'initial',
'unset',
'black',
'white',
'red',
'green',
'blue',
'yellow',
'cyan',
'magenta',
'gray',
'grey',
'darkgray',
'darkgrey',
'lightgray',
'lightgrey',
'orange',
'purple',
'brown',
'pink',
'lime',
'navy',
'teal',
'silver',
'maroon',
'olive',
'aqua',
'fuchsia',
]);

This comment was marked as resolved.

Comment on lines +910 to +927
// Try to fetch theme marked as system default (is_system_default=true)
const defaultResponse = await fetch(
'/api/v1/theme/?q=(filters:!((col:is_system_default,opr:eq,value:!t)))',
);
if (defaultResponse.ok) {
const data = await defaultResponse.json();
if (data.result?.length > 0) {
const themeConfig = JSON.parse(data.result[0].json_data);
if (themeConfig && typeof themeConfig === 'object') {
return themeConfig;
}
}
}

// Fallback: Try to fetch system theme named 'THEME_DEFAULT'
const fallbackResponse = await fetch(
'/api/v1/theme/?q=(filters:!((col:theme_name,opr:eq,value:THEME_DEFAULT),(col:is_system,opr:eq,value:!t)))',
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sequential API calls in theme fallback logic category Performance

Tell me more
What is the issue?

The fallback mechanism makes two sequential API calls even when the first call succeeds but returns an empty result set, creating unnecessary network overhead.

Why this matters

When the first API call returns successfully but with no matching themes, the code still makes a second API call. This doubles the network latency and server load during theme recovery scenarios, which are already error conditions that should be handled efficiently.

Suggested change ∙ Feature Preview

Combine both queries into a single API call using OR logic, or add an early return when the first call indicates no themes exist:

// Single API call with OR condition for both system default and named fallback
const response = await fetch(
  '/api/v1/theme/?q=(filters:!((col:is_system_default,opr:eq,value:!t),(col:theme_name,opr:eq,value:THEME_DEFAULT)))',
);
if (response.ok) {
  const data = await response.json();
  if (data.result?.length > 0) {
    // Prioritize system default if available
    const systemDefault = data.result.find(theme => theme.is_system_default);
    const theme = systemDefault || data.result[0];
    const themeConfig = JSON.parse(theme.json_data);
    if (themeConfig && typeof themeConfig === 'object') {
      return themeConfig;
    }
  }
}
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

@dosubot dosubot bot added change:frontend Requires changing the frontend global:theming Related to theming Superset labels Oct 1, 2025
@rusackas
Copy link
Member

rusackas commented Oct 1, 2025

Do we really need the feature flag? I think everyone is in favor of more validation :) Seems like it should be default behavior.

@rebenitez1802
Copy link
Contributor Author

rebenitez1802 commented Oct 2, 2025

Do we really need the feature flag? I think everyone is in favor of more validation :) Seems like it should be default behavior.

My thought process was because is not a small iteration and also the rules could require a little more refinement it was safer to put it behind a FF but if everyone agrees I can remove it.

@rebenitez1802 rebenitez1802 force-pushed the feat/theme-error-handling branch from 4204948 to 61323a0 Compare October 10, 2025 16:19
@geido geido added the hold:testing! On hold for testing label Oct 13, 2025
@gabotorresruiz
Copy link
Contributor

I really appreciate the effort to improve theme validation, but I’m concerned about the long-term maintainability of this approach. Here are my thoughts:

Ant Design silently ignores invalid tokens by design. It's permissive and forward compatible. This means:

  • Token typos --> ignored by AntD, defaults are used
  • "#invalid" values --> ignored by AntD, defaults are used
  • Unknown tokens from future versions --> continue to work without validation updates

By introducing custom validators (useThemeValidation.ts, themeTokenValidation.ts), we're essentially:

  1. Reimplementing Ant Design's token system within our own validation layer
  2. Adding maintenance overhead. Every Ant Design update would require updating our validators
  3. Risking incompatibility. Valid future tokens could be rejected by our outdated validation

So I think we should keep it simple and only have structural validations:

  • JSON syntax validation
  • Empty theme blocking
  • Algorithm type validation ("dark" | "light" | "system")
  • Object type validation (token, components)

So the ENHANCED_THEME_VALIDATION feature flag, even though it’s optional, I think that if we make an structural validation, we might not need it anymore.

To sum up, I think we should just simplify to:

  • Basic structural validation (syntax, type, emptiness)
  • Runtime fallback if theme loading fails
  • User notifications for load failures

This gives users clear feedback while avoiding the complexity of duplicating Ant Design’s validation logic.

What do you think? Happy to discuss trade-offs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:frontend Requires changing the frontend global:theming Related to theming Superset hold:testing! On hold for testing packages size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants